diff --git a/benchmarks/mlir/fp32-pack-gemm-operand-a-512x1024.mlir b/benchmarks/mlir/fp32-pack-gemm-operand-a-512x1024.mlir index f8f0cf7ef..5b8e9b13a 100644 --- a/benchmarks/mlir/fp32-pack-gemm-operand-a-512x1024.mlir +++ b/benchmarks/mlir/fp32-pack-gemm-operand-a-512x1024.mlir @@ -4,7 +4,7 @@ // BENCH_TOTAL_FLOPS: 2097152 func.func @entry(%arg0: tensor<512x1024xf32>, %arg1: tensor<16x32x32x32xf32>) -> tensor<16x32x32x32xf32> { - %pack = tensor.pack %arg0 + %pack = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<512x1024xf32> -> tensor<16x32x32x32xf32> diff --git a/benchmarks/mlir/fp32-pack-gemm-operand-b-512x1024.mlir b/benchmarks/mlir/fp32-pack-gemm-operand-b-512x1024.mlir index 08254ec05..5d44e36f9 100644 --- a/benchmarks/mlir/fp32-pack-gemm-operand-b-512x1024.mlir +++ b/benchmarks/mlir/fp32-pack-gemm-operand-b-512x1024.mlir @@ -4,7 +4,7 @@ // BENCH_TOTAL_FLOPS: 2097152 func.func @entry(%arg0: tensor<1024x512xf32>, %arg1: tensor<16x32x32x32xf32>) -> tensor<16x32x32x32xf32> { - %0 = tensor.pack %arg0 + %0 = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] diff --git a/benchmarks/mlir/fp32-unpack-gemm-operand-a-512x512.mlir b/benchmarks/mlir/fp32-unpack-gemm-operand-a-512x512.mlir index 112a8db7c..23c30326b 100644 --- a/benchmarks/mlir/fp32-unpack-gemm-operand-a-512x512.mlir +++ b/benchmarks/mlir/fp32-unpack-gemm-operand-a-512x512.mlir @@ -4,7 +4,7 @@ // BENCH_TOTAL_FLOPS: 1048576 func.func @entry(%arg0: tensor<16x16x32x32xf32>, %arg1: tensor<512x512xf32>) -> tensor<512x512xf32> { - %unpack = tensor.unpack %arg0 + %unpack = linalg.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<16x16x32x32xf32> -> tensor<512x512xf32> diff --git a/build_tools/llvm_version.txt b/build_tools/llvm_version.txt index 40d75418f..62db6b0c0 100644 --- a/build_tools/llvm_version.txt +++ b/build_tools/llvm_version.txt @@ -1 +1 @@ -3654f1baa66f524c89e40ab24e18e594e56363e9 +2b71df5a74cb5bd67f3f34277749dc920fd35105 diff --git a/docs/TPPDialect.md b/docs/TPPDialect.md index 36cb2ea1a..ac9c531f8 100644 --- a/docs/TPPDialect.md +++ b/docs/TPPDialect.md @@ -172,12 +172,12 @@ Should be fused with the user(s). GEMM ops have transposed versions, we should use this op to annotate operands. ## Tensor pack -The tensor operation `tensor.pack` does a "block transpose" (n,m <-> m,n) copies. +The tensor operation `linalg.pack` does a "block transpose" (n,m <-> m,n) copies. We lower this to a series of `tpp.copy` into temporary tiles if needed. But the idea is that all constant tensors would have been packed by the compiler already and all input packs would be combined at the beginning. ## Tensor Unpack -The tensor operation `tensor.unpack` does a "block transpose" (n,m <-> m,n) copies. +The tensor operation `linalg.unpack` does a "block transpose" (n,m <-> m,n) copies. ## VNNI Pack Packs into VNNI shape. diff --git a/include/TPP/IR/StructuredOpMatcher.h b/include/TPP/IR/StructuredOpMatcher.h index 7b68e5010..317d867c6 100644 --- a/include/TPP/IR/StructuredOpMatcher.h +++ b/include/TPP/IR/StructuredOpMatcher.h @@ -190,7 +190,7 @@ struct HasStaticStrides { SmallVector strides; if (auto memRefType = dyn_cast_or_null(operandType)) { int64_t offset; - if (failed(getStridesAndOffset(memRefType, strides, offset))) + if (failed(memRefType.getStridesAndOffset(strides, offset))) return false; if (llvm::any_of(strides, [](int64_t stride) { return stride == ShapedType::kDynamic; diff --git a/include/TPP/Passes.td b/include/TPP/Passes.td index fe625a6e0..f19cf5a47 100644 --- a/include/TPP/Passes.td +++ b/include/TPP/Passes.td @@ -262,26 +262,26 @@ def CombineXsmmOpPass : Pass<"combine-xsmm-op-optimization", "func::FuncOp"> { } def PropagatePackUnPack : Pass<"propagate-pack-and-unpack", "func::FuncOp"> { - let summary = "Propagate tensor.pack and tensor.unpack"; + let summary = "Propagate linalg.pack and linalg.unpack"; let description = [{ - Attempt to push tensor.pack and tensor.unpack at the boundaries. Currently, + Attempt to push linalg.pack and linalg.unpack at the boundaries. Currently, it propagates through linalg element-wise operations. Only one operand in the - generic must come from a tensor.pack/tensor.unpack. + generic must come from a linalg.pack/linalg.unpack. }]; } def SimplifyAndCanonicalizePack : Pass<"simplify-pack", "func::FuncOp"> { - let summary = "Simplify and canonicalize tensor.pack"; + let summary = "Simplify and canonicalize linalg.pack"; let description = [{ - Apply `tensor.pack` and `tensor.unpack` canonicalization and simplification + Apply `linalg.pack` and `linalg.unpack` canonicalization and simplification patterns. }]; } def ConstantFoldPack : Pass<"constant-fold-pack", "ModuleOp"> { - let summary = "Constant fold tensor.pack"; + let summary = "Constant fold linalg.pack"; let description = [{ - Reduce pack overhead by folding tensor.pack into constant tensors. + Reduce pack overhead by folding linalg.pack into constant tensors. }]; let dependentDialects = ["linalg::LinalgDialect", "tensor::TensorDialect", diff --git a/lib/TPP/Conversion/ConvertCheckToLoops/ConvertCheckToLoops.cpp b/lib/TPP/Conversion/ConvertCheckToLoops/ConvertCheckToLoops.cpp index 9d349dc39..7a82e5c7f 100644 --- a/lib/TPP/Conversion/ConvertCheckToLoops/ConvertCheckToLoops.cpp +++ b/lib/TPP/Conversion/ConvertCheckToLoops/ConvertCheckToLoops.cpp @@ -184,7 +184,7 @@ struct ConvertCheckToLoops void runOnOperation() override { RewritePatternSet patterns(&getContext()); populateCheckToLoopsPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Conversion/ConvertLinalgToFunc/ConvertLinalgToFunc.cpp b/lib/TPP/Conversion/ConvertLinalgToFunc/ConvertLinalgToFunc.cpp index b8ca7d841..c8e819a2a 100644 --- a/lib/TPP/Conversion/ConvertLinalgToFunc/ConvertLinalgToFunc.cpp +++ b/lib/TPP/Conversion/ConvertLinalgToFunc/ConvertLinalgToFunc.cpp @@ -127,7 +127,7 @@ struct ConvertLinalgToFunc auto *ctx = &getContext(); RewritePatternSet patterns(ctx); patterns.add(ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Conversion/ConvertLinalgToXsmm/ConvertLinalgToXsmm.cpp b/lib/TPP/Conversion/ConvertLinalgToXsmm/ConvertLinalgToXsmm.cpp index c9166341f..4d6cd370f 100644 --- a/lib/TPP/Conversion/ConvertLinalgToXsmm/ConvertLinalgToXsmm.cpp +++ b/lib/TPP/Conversion/ConvertLinalgToXsmm/ConvertLinalgToXsmm.cpp @@ -585,7 +585,7 @@ static FailureOr checkAccess(linalg::LinalgOp linalgOp, unsigned m, strideB = (*stridesOnB)[*batchPosCodomainB]; } - auto loops = linalgOp.computeStaticLoopSizes(); + auto loops = linalgOp.getStaticLoopRanges(); int64_t batchVal = (batchPos) ? loops[batchPos.value()] : 0; bool isVnni = vnni::utils::isInVnniLayout(linalgOp); @@ -847,7 +847,7 @@ void ConvertLinalgToXsmm::runOnOperation() { SmallVector skipPatterns(skipOperations.begin(), skipOperations.end()); tpp::populateLinalgToXsmmPatterns(patterns, skipPatterns); - if (failed(applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)))) + if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) return signalPassFailure(); } diff --git a/lib/TPP/Conversion/ConvertPerfToFunc/ConvertPerfToFunc.cpp b/lib/TPP/Conversion/ConvertPerfToFunc/ConvertPerfToFunc.cpp index ccfca5d43..24c48fc7f 100644 --- a/lib/TPP/Conversion/ConvertPerfToFunc/ConvertPerfToFunc.cpp +++ b/lib/TPP/Conversion/ConvertPerfToFunc/ConvertPerfToFunc.cpp @@ -242,7 +242,7 @@ struct ConvertPerfToFunc void runOnOperation() override { RewritePatternSet patterns(&getContext()); populatePerfToFuncPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Conversion/ConvertPerfToLoops/ConvertPerfToLoops.cpp b/lib/TPP/Conversion/ConvertPerfToLoops/ConvertPerfToLoops.cpp index ef3eb7ccc..640efed69 100644 --- a/lib/TPP/Conversion/ConvertPerfToLoops/ConvertPerfToLoops.cpp +++ b/lib/TPP/Conversion/ConvertPerfToLoops/ConvertPerfToLoops.cpp @@ -105,7 +105,7 @@ struct ConvertPerfToLoops void runOnOperation() override { RewritePatternSet patterns(&getContext()); populatePerfToLoopsPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Conversion/ConvertVectorToXsmm/ConvertVectorToXsmm.cpp b/lib/TPP/Conversion/ConvertVectorToXsmm/ConvertVectorToXsmm.cpp index b6f2ec3ee..3daf1155c 100644 --- a/lib/TPP/Conversion/ConvertVectorToXsmm/ConvertVectorToXsmm.cpp +++ b/lib/TPP/Conversion/ConvertVectorToXsmm/ConvertVectorToXsmm.cpp @@ -248,7 +248,7 @@ struct ConvertVectorToXsmm void runOnOperation() final { PatternRewriter rewriter(&getContext()); - if (failed(applyPatternsAndFoldGreedily(getOperation(), patterns))) { + if (failed(applyPatternsGreedily(getOperation(), patterns))) { signalPassFailure(); } } diff --git a/lib/TPP/Conversion/ConvertXsmmToFunc/ConvertXsmmToFunc.cpp b/lib/TPP/Conversion/ConvertXsmmToFunc/ConvertXsmmToFunc.cpp index 1c67a5c58..cfd2874c8 100644 --- a/lib/TPP/Conversion/ConvertXsmmToFunc/ConvertXsmmToFunc.cpp +++ b/lib/TPP/Conversion/ConvertXsmmToFunc/ConvertXsmmToFunc.cpp @@ -432,7 +432,7 @@ struct ConvertXsmmToFunc ConvertGemmDispatchOp, ConvertBrgemmDispatchOp, ConvertFusedBrgemmOp, ConvertIntelAMXTileConfigDispatchOp>( patterns.getContext()); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/DefaultPipeline.cpp b/lib/TPP/DefaultPipeline.cpp index 45488eb30..d17b033a0 100644 --- a/lib/TPP/DefaultPipeline.cpp +++ b/lib/TPP/DefaultPipeline.cpp @@ -192,25 +192,28 @@ struct DefaultPipeline : public tpp::impl::DefaultPipelineBase, options.amx = vnni::utils::hasAMX(); pm.addPass(createConvertVectorToLLVMPass(options)); pm.addPass(createFinalizeMemRefToLLVMConversionPass()); - pm.addPass(createConvertSCFToCFPass()); + pm.addPass(createSCFToControlFlowPass()); if (defParallel) pm.addPass(createConvertOpenMPToLLVMPass()); - pm.addPass(createConvertMathToLLVMPass()); pm.addNestedPass(createGpuAsyncRegionPass()); pm.addPass(createGpuToLLVMConversionPass()); GpuModuleToBinaryPassOptions gpuModuleToBinaryPassOptions; gpuModuleToBinaryPassOptions.compilationTarget = "fatbin"; pm.addPass(createGpuModuleToBinaryPass(gpuModuleToBinaryPassOptions)); + pm.addPass(createConvertMathToLLVMPass()); pm.addPass(createAsyncToAsyncRuntimePass()); pm.addPass(createAsyncRuntimeRefCountingPass()); pm.addPass(createConvertAsyncToLLVMPass()); + pm.addPass(createConvertIndexToLLVMPass()); pm.addPass(createConvertFuncToLLVMPass()); - pm.addNestedPass(createArithToLLVMConversionPass()); - pm.addNestedPass(createCanonicalizerPass()); - pm.addNestedPass(createCSEPass()); + pm.addPass(createArithToLLVMConversionPass()); + pm.addPass(createConvertControlFlowToLLVMPass()); + pm.addPass(createUBToLLVMConversionPass()); + pm.addPass(createCanonicalizerPass()); + pm.addPass(createCSEPass()); pm.addPass(createReconcileUnrealizedCastsPass()); // Anything useful has been lowered by now. diff --git a/lib/TPP/DefaultTppPasses.cpp b/lib/TPP/DefaultTppPasses.cpp index 1e3b3d3b7..615396e31 100644 --- a/lib/TPP/DefaultTppPasses.cpp +++ b/lib/TPP/DefaultTppPasses.cpp @@ -104,7 +104,7 @@ struct DefaultTppPasses if (linalgToLoops) { // Lower linalg directly to loops. // Skip all TPP transformations. - // Generalize tensor.pack and tensor.unpack. + // Generalize linalg.pack and linalg.unpack. pm.addPass(createLowerPacksAndUnPacks()); pm.addNestedPass(createDecomposeAggregatedOps()); pm.addPass(createBufferize()); @@ -120,7 +120,7 @@ struct DefaultTppPasses TppMappingOptions tppMappingOptions{lowerPackUnpackWithoutTranspose}; pm.addPass(createTppMapping(tppMappingOptions)); - // Generalize tensor.pack and tensor.unpack. + // Generalize linalg.pack and linalg.unpack. pm.addPass(createLowerPacksAndUnPacks()); pm.addPass(createCleanup()); diff --git a/lib/TPP/Dialect/Xsmm/XsmmUtils.cpp b/lib/TPP/Dialect/Xsmm/XsmmUtils.cpp index 70f915329..ad9ec7119 100644 --- a/lib/TPP/Dialect/Xsmm/XsmmUtils.cpp +++ b/lib/TPP/Dialect/Xsmm/XsmmUtils.cpp @@ -151,7 +151,7 @@ getVectorUnaryInfo(MemRefType shapedType, MemRefType inputType, SmallVector strides; int64_t offset; - if (failed(getStridesAndOffset(memrefType, strides, offset))) { + if (failed(memrefType.getStridesAndOffset(strides, offset))) { return failure(); } if (strides.empty()) { diff --git a/lib/TPP/GPU/GpuConversion.cpp b/lib/TPP/GPU/GpuConversion.cpp index 806eebeab..8de7958d2 100644 --- a/lib/TPP/GPU/GpuConversion.cpp +++ b/lib/TPP/GPU/GpuConversion.cpp @@ -58,7 +58,7 @@ struct GpuConversion : public tpp::impl::GpuConversionBase, void constructPipeline() override { // Map loops into GPU kernels. pm.addNestedPass(createGpuMapParallelLoopsPass()); - pm.addNestedPass(createParallelLoopToGpuPass()); + pm.addNestedPass(createConvertParallelLoopToGpuPass()); pm.addPass(createCleanup()); // First lower linalg using custom patterns then fall back to diff --git a/lib/TPP/GPU/GpuDataTransfer.cpp b/lib/TPP/GPU/GpuDataTransfer.cpp index 3904b2a20..c53a5f468 100644 --- a/lib/TPP/GPU/GpuDataTransfer.cpp +++ b/lib/TPP/GPU/GpuDataTransfer.cpp @@ -238,7 +238,7 @@ class GpuDataTransfer : public tpp::impl::GpuDataTransferBase { RewritePatternSet patterns(ctx); // TODO: Add cleanup patterns to minimize data copies. patterns.add(ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/GPU/GpuInlineConstants.cpp b/lib/TPP/GPU/GpuInlineConstants.cpp index 923a9c97c..ba50380e4 100644 --- a/lib/TPP/GPU/GpuInlineConstants.cpp +++ b/lib/TPP/GPU/GpuInlineConstants.cpp @@ -81,7 +81,7 @@ struct GpuInlineConstants void runOnOperation() override { RewritePatternSet patterns(&getContext()); populateGpuInlineConstantsPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/GPU/GpuToCuda.cpp b/lib/TPP/GPU/GpuToCuda.cpp index 07cd785b9..f8f21798b 100644 --- a/lib/TPP/GPU/GpuToCuda.cpp +++ b/lib/TPP/GPU/GpuToCuda.cpp @@ -68,7 +68,7 @@ struct GpuToCuda : public tpp::impl::GpuToCudaBase, pm.addNestedPass(arith::createArithExpandOpsPass()); pm.addNestedPass(createLowerAffinePass()); pm.addNestedPass(createConvertVectorToSCFPass()); - pm.addNestedPass(createConvertSCFToCFPass()); + pm.addNestedPass(createSCFToControlFlowPass()); pm.addNestedPass(createConvertNVGPUToNVVMPass()); pm.addNestedPass(createConvertGpuOpsToNVVMOps()); @@ -77,6 +77,7 @@ struct GpuToCuda : public tpp::impl::GpuToCudaBase, pm.addNestedPass(createConvertFuncToLLVMPass()); pm.addNestedPass(createArithToLLVMConversionPass()); pm.addNestedPass(createConvertIndexToLLVMPass()); + pm.addNestedPass(createUBToLLVMConversionPass()); GpuNVVMAttachTargetOptions nvvmTargetOptions; nvvmTargetOptions.triple = gpuTriple; @@ -85,7 +86,6 @@ struct GpuToCuda : public tpp::impl::GpuToCudaBase, pm.addPass(createGpuNVVMAttachTarget(nvvmTargetOptions)); // Create CUDA kernels. - pm.addNestedPass(createStripDebugInfoPass()); pm.addNestedPass(createCanonicalizerPass()); pm.addNestedPass(createCSEPass()); pm.addNestedPass(createReconcileUnrealizedCastsPass()); diff --git a/lib/TPP/GPU/GpuVectorize.cpp b/lib/TPP/GPU/GpuVectorize.cpp index 04888eddb..2a19e3df1 100644 --- a/lib/TPP/GPU/GpuVectorize.cpp +++ b/lib/TPP/GPU/GpuVectorize.cpp @@ -109,7 +109,7 @@ struct GpuVectorize : public tpp::impl::GpuVectorizeBase { vector::TransferReadOp::getCanonicalizationPatterns(patterns, ctx); vector::TransferWriteOp::getCanonicalizationPatterns(patterns, ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/GPU/LinalgToXeGPU.cpp b/lib/TPP/GPU/LinalgToXeGPU.cpp index 243a9174d..cfa15b240 100644 --- a/lib/TPP/GPU/LinalgToXeGPU.cpp +++ b/lib/TPP/GPU/LinalgToXeGPU.cpp @@ -884,7 +884,7 @@ static LogicalResult createDPASKernel(linalg::LinalgOp linalgOp, // DPAS only works with F32 accumulators. auto dpasResType = - VectorType::get(dpasTypeC.getShape(), FloatType::getF32(ctx)); + VectorType::get(dpasTypeC.getShape(), Float32Type::get(ctx)); // Extend the accumulation values if needed. auto convOutPrecision = !typeC.getElementType().isF32(); @@ -1397,12 +1397,12 @@ struct LinalgToXeGPU : public tpp::impl::LinalgToXeGPUBase { // Run GEMM pattern first to allow fusion with its consumers. RewritePatternSet gemmPatterns(&getContext()); populateLinalgGemmToXeGPUPatterns(gemmPatterns, options); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(gemmPatterns)); + (void)applyPatternsGreedily(getOperation(), std::move(gemmPatterns)); // Convert other remaining ops. RewritePatternSet patterns(&getContext()); populateLinalgEltwiseToXeGPUPatterns(patterns, options); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Runner/MLIRBench.cpp b/lib/TPP/Runner/MLIRBench.cpp index dead56af8..64bd992ad 100644 --- a/lib/TPP/Runner/MLIRBench.cpp +++ b/lib/TPP/Runner/MLIRBench.cpp @@ -88,13 +88,13 @@ LogicalResult MLIRBench::findKernel(StringRef name) { } else { // If there is no entry function, and multiple functions, bail - return module.emitError("No valid entry point, use mlir-cpu-runner"); + return module.emitError("No valid entry point, use mlir-runner"); } // Ignore functions that return more than one result auto funcType = kernel.getFunctionType(); if (funcType.getNumResults() > 1) - return module.emitError("Multiple return values, use mlir-cpu-runner"); + return module.emitError("Multiple return values, use mlir-runner"); return success(); } diff --git a/lib/TPP/Transforms/BrgemmLinalgTiling.cpp b/lib/TPP/Transforms/BrgemmLinalgTiling.cpp index 861800697..7f8d07e00 100644 --- a/lib/TPP/Transforms/BrgemmLinalgTiling.cpp +++ b/lib/TPP/Transforms/BrgemmLinalgTiling.cpp @@ -230,8 +230,7 @@ struct BrgemmLinalgTiling : public tpp::impl::BrgemmLinalgTilingBase { - using OpRewritePattern::OpRewritePattern; +// Helper pattern - lower linalg.pack operations that pack constants. +struct LowerConstantPacking : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(tensor::PackOp packOp, + LogicalResult matchAndRewrite(linalg::PackOp packOp, PatternRewriter &rewriter) const override { auto constOp = packOp.getSource().getDefiningOp(); if (!constOp) @@ -52,7 +52,7 @@ struct LowerConstantPacking : public OpRewritePattern { return rewriter.notifyMatchFailure( packOp, "expects destination with static shape"); - // If it is a splat constant, skip and let tensor.pack folder to handle this + // If it is a splat constant, skip and let linalg.pack folder to handle this // case. if (denseAttr.isSplat()) return rewriter.notifyMatchFailure( @@ -77,13 +77,13 @@ struct ConstantFoldPack // Apply canonicalization to fold trivial cases and linalg constant folders // to cleanup lowered packs. linalg::FillOp::getCanonicalizationPatterns(patterns, ctx); - tensor::PackOp::getCanonicalizationPatterns(patterns, ctx); + linalg::PackOp::getCanonicalizationPatterns(patterns, ctx); tensor::populateRewriteAsConstantPatterns( patterns, [](OpOperand *) -> bool { return true; }); linalg::populateConstantFoldLinalgOperations( patterns, [](OpOperand *) -> bool { return true; }); - (void)applyPatternsAndFoldGreedily(module, std::move(patterns)); + (void)applyPatternsGreedily(module, std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/ConvInitSimplify.cpp b/lib/TPP/Transforms/ConvInitSimplify.cpp index bd5a43d89..66c79451c 100644 --- a/lib/TPP/Transforms/ConvInitSimplify.cpp +++ b/lib/TPP/Transforms/ConvInitSimplify.cpp @@ -114,7 +114,7 @@ struct ConvInitSimplify void runOnOperation() override { RewritePatternSet patterns(getOperation().getContext()); patterns.add(patterns.getContext()); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/ConvertForAllToParallelOp.cpp b/lib/TPP/Transforms/ConvertForAllToParallelOp.cpp index f80a4f081..b5e78d491 100644 --- a/lib/TPP/Transforms/ConvertForAllToParallelOp.cpp +++ b/lib/TPP/Transforms/ConvertForAllToParallelOp.cpp @@ -40,7 +40,7 @@ struct ConvertForAllToParallelOp void runOnOperation() override { RewritePatternSet patterns(getOperation().getContext()); patterns.add(patterns.getContext()); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/ConvertLinalgToInplace.cpp b/lib/TPP/Transforms/ConvertLinalgToInplace.cpp index 8d35688bc..a1d150a0d 100644 --- a/lib/TPP/Transforms/ConvertLinalgToInplace.cpp +++ b/lib/TPP/Transforms/ConvertLinalgToInplace.cpp @@ -140,7 +140,7 @@ struct ConvertLinalgToInplace void runOnOperation() override { RewritePatternSet patterns(&getContext()); populateCombinePatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/DecomposeAggregatedOps.cpp b/lib/TPP/Transforms/DecomposeAggregatedOps.cpp index f1ce7a087..f1b6c9dad 100644 --- a/lib/TPP/Transforms/DecomposeAggregatedOps.cpp +++ b/lib/TPP/Transforms/DecomposeAggregatedOps.cpp @@ -43,7 +43,7 @@ struct DecomposeAggregatedOps void runOnOperation() override { RewritePatternSet patterns(getOperation().getContext()); patterns.add(patterns.getContext()); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/FoldAddIntoDest.cpp b/lib/TPP/Transforms/FoldAddIntoDest.cpp index 165cedd41..e840855ee 100644 --- a/lib/TPP/Transforms/FoldAddIntoDest.cpp +++ b/lib/TPP/Transforms/FoldAddIntoDest.cpp @@ -122,7 +122,7 @@ struct FoldAddIntoDest RewritePatternSet patterns(ctx); patterns.add(ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/FoldIntoEltwise.cpp b/lib/TPP/Transforms/FoldIntoEltwise.cpp index 9f3f3bbfd..703e49ab4 100644 --- a/lib/TPP/Transforms/FoldIntoEltwise.cpp +++ b/lib/TPP/Transforms/FoldIntoEltwise.cpp @@ -192,7 +192,7 @@ struct FoldIntoEltwise : tpp::impl::FoldIntoEltwiseBase { void runOnOperation() override { RewritePatternSet patterns(&getContext()); patterns.add(patterns.getContext()); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/HoistVectorTransfers.cpp b/lib/TPP/Transforms/HoistVectorTransfers.cpp index 22ba8f9cc..d6dd2f459 100644 --- a/lib/TPP/Transforms/HoistVectorTransfers.cpp +++ b/lib/TPP/Transforms/HoistVectorTransfers.cpp @@ -251,8 +251,7 @@ struct HoistVectorTransfers populateHoistVectorTransferPatterns(patterns); GreedyRewriteConfig config; config.strictMode = GreedyRewriteStrictness::ExistingOps; - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns), - config); + (void)applyPatternsGreedily(getOperation(), std::move(patterns), config); } }; } // namespace tpp diff --git a/lib/TPP/Transforms/IntelAMXTileConfig.cpp b/lib/TPP/Transforms/IntelAMXTileConfig.cpp index cf0c4ae24..c708a4f16 100644 --- a/lib/TPP/Transforms/IntelAMXTileConfig.cpp +++ b/lib/TPP/Transforms/IntelAMXTileConfig.cpp @@ -93,7 +93,7 @@ struct IntelAMXTileConfig : OpRewritePattern { auto alloca = rewriter.create( op.getLoc(), MemRefType::get({64}, rewriter.getI8Type())); - ValueRange tileConfigInputs{alloca}; + SmallVector tileConfigInputs{alloca}; rewriter.create( op.getLoc(), tileConfigSetup, tileConfigInputs); @@ -107,7 +107,7 @@ struct IntelAMXTileConfig : OpRewritePattern { xsmm::utils::getDataType(rewriter, op.getOperand(1).getType()), invokeOperands); - ValueRange tileResetInputs{alloca}; + SmallVector tileResetInputs{alloca}; rewriter.create( op.getLoc(), tileConfigReset, tileResetInputs); @@ -132,7 +132,7 @@ struct IntelAMXTileConfigInsertionPass void runOnOperation() override { RewritePatternSet patterns(&getContext()); populateCombinePatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; } // namespace tpp diff --git a/lib/TPP/Transforms/IntelAMXTileConfigHoisting.cpp b/lib/TPP/Transforms/IntelAMXTileConfigHoisting.cpp index df0ba42c1..145b33ac4 100644 --- a/lib/TPP/Transforms/IntelAMXTileConfigHoisting.cpp +++ b/lib/TPP/Transforms/IntelAMXTileConfigHoisting.cpp @@ -94,7 +94,7 @@ struct IntelAMXTileConfigHoistingPass void runOnOperation() override { RewritePatternSet patterns(&getContext()); populateCombinePatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; } // namespace tpp diff --git a/lib/TPP/Transforms/LinalgConvertCompareSelectToMaximumfPass.cpp b/lib/TPP/Transforms/LinalgConvertCompareSelectToMaximumfPass.cpp index 5ce760fec..624cb6a64 100644 --- a/lib/TPP/Transforms/LinalgConvertCompareSelectToMaximumfPass.cpp +++ b/lib/TPP/Transforms/LinalgConvertCompareSelectToMaximumfPass.cpp @@ -74,7 +74,7 @@ struct LinalgConvertCompareSelectToMaximumfPass void runOnOperation() override { RewritePatternSet patterns(&getContext()); populateCombinePatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; } // namespace tpp diff --git a/lib/TPP/Transforms/LinalgDeGeneralize.cpp b/lib/TPP/Transforms/LinalgDeGeneralize.cpp index 3681a7b5a..315b69e7b 100644 --- a/lib/TPP/Transforms/LinalgDeGeneralize.cpp +++ b/lib/TPP/Transforms/LinalgDeGeneralize.cpp @@ -33,7 +33,7 @@ struct LinalgDeGeneralize func::FuncOp func = getOperation(); RewritePatternSet patterns(&getContext()); linalg::populateLinalgDeGeneralizationPatterns(patterns); - (void)applyPatternsAndFoldGreedily(func.getBody(), std::move(patterns)); + (void)applyPatternsGreedily(func.getBody(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/LowerPacksAndUnpacks.cpp b/lib/TPP/Transforms/LowerPacksAndUnpacks.cpp index cc135512f..6c65cb6b7 100644 --- a/lib/TPP/Transforms/LowerPacksAndUnpacks.cpp +++ b/lib/TPP/Transforms/LowerPacksAndUnpacks.cpp @@ -35,7 +35,7 @@ namespace { template static SmallVector getTileSizes(OpTy packingOp, bool isConsumer = false) { - static_assert(llvm::is_one_of::value, + static_assert(llvm::is_one_of::value, "applies to only pack or unpack operations"); SmallVector tiledDims = llvm::to_vector(packingOp.getInnerDimsPos()); assert(!tiledDims.empty()); @@ -43,7 +43,7 @@ static SmallVector getTileSizes(OpTy packingOp, int64_t upTo = *std::min_element(tiledDims.begin(), tiledDims.end()); return SmallVector(upTo, 1); } - if (std::is_same::value) { + if (std::is_same::value) { int64_t upTo = packingOp.getDestType().getRank() - 2; return SmallVector(upTo, 1); } @@ -71,20 +71,20 @@ static FailureOr tileOp(RewriterBase &rewriter, // Fuse producer and consumer pack. Standalone packs are tiled into 2d tiles. static void fuseOrTilePacks(RewriterBase &rewriter, FunctionOpInterface func) { - SmallVector chainedPackOps; - SmallVector otherPacks; - SmallVector unPacks; - func->walk([&](tensor::PackOp consumerPackOp) { + SmallVector chainedPackOps; + SmallVector otherPacks; + SmallVector unPacks; + func->walk([&](linalg::PackOp consumerPackOp) { Value source = consumerPackOp.getSource(); - tensor::PackOp producerPackOp = - dyn_cast_or_null(source.getDefiningOp()); + linalg::PackOp producerPackOp = + dyn_cast_or_null(source.getDefiningOp()); if (producerPackOp) chainedPackOps.push_back(consumerPackOp); else otherPacks.push_back(consumerPackOp); }); func->walk( - [&](tensor::UnPackOp unpackOp) { unPacks.push_back(unpackOp); }); + [&](linalg::UnPackOp unpackOp) { unPacks.push_back(unpackOp); }); // Tile and fuse. for (auto consumerPackOp : chainedPackOps) { @@ -98,7 +98,7 @@ static void fuseOrTilePacks(RewriterBase &rewriter, FunctionOpInterface func) { tileSizes); if (failed(tilingResult)) continue; - auto tiledPack = dyn_cast(tilingResult->tiledOps.back()); + auto tiledPack = dyn_cast(tilingResult->tiledOps.back()); assert(tiledPack); // Step 3. Fuse consumer and producer. auto forLoops = @@ -112,7 +112,7 @@ static void fuseOrTilePacks(RewriterBase &rewriter, FunctionOpInterface func) { forLoops); if (!fusedProducer) continue; - rewriter.replaceOp(consumerPackOp, tilingResult->replacements); + rewriter.replaceOp(consumerPackOp, tilingResult->mergeResult.replacements); } // Tile packs. @@ -124,7 +124,7 @@ static void fuseOrTilePacks(RewriterBase &rewriter, FunctionOpInterface func) { rewriter, cast(packOp.getOperation()), tileSizes); if (failed(tilingResult)) continue; - rewriter.replaceOp(packOp, tilingResult->replacements); + rewriter.replaceOp(packOp, tilingResult->mergeResult.replacements); } // Tile unpacks. @@ -136,16 +136,16 @@ static void fuseOrTilePacks(RewriterBase &rewriter, FunctionOpInterface func) { rewriter, cast(unPackOp.getOperation()), tileSizes); if (failed(tilingResult)) continue; - rewriter.replaceOp(unPackOp, tilingResult->replacements); + rewriter.replaceOp(unPackOp, tilingResult->mergeResult.replacements); } } -// A wrapper pattern that calls linalg::lowerPack on tensor::PackOp. It lowers -// a tensor.pack op to tensor.pad + tensor.expand_shape + linalg.transpose ops. -struct LowerPackPattern : public OpRewritePattern { - using OpRewritePattern::OpRewritePattern; +// A wrapper pattern that calls linalg::lowerPack on linalg::PackOp. It lowers +// a linalg.pack op to tensor.pad + tensor.expand_shape + linalg.transpose ops. +struct LowerPackPattern : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(tensor::PackOp op, + LogicalResult matchAndRewrite(linalg::PackOp op, PatternRewriter &rewriter) const override { FailureOr res = linalg::lowerPack(rewriter, op); if (failed(res)) { @@ -156,13 +156,13 @@ struct LowerPackPattern : public OpRewritePattern { } }; -// A wrapper pattern that calls linalg::lowerUnPack on tensor::UnPackOp. It -// lowers a tensor.unpack op to tensor.empty + linalg.transpose + +// A wrapper pattern that calls linalg::lowerUnPack on linalg::UnPackOp. It +// lowers a linalg.unpack op to tensor.empty + linalg.transpose + // tensor.collapse_shape + tensor.extract_slice ops. -struct LowerUnPackPattern : public OpRewritePattern { - using OpRewritePattern::OpRewritePattern; +struct LowerUnPackPattern : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(tensor::UnPackOp op, + LogicalResult matchAndRewrite(linalg::UnPackOp op, PatternRewriter &rewriter) const override { if (failed(linalg::lowerUnPack(rewriter, op))) { return rewriter.notifyMatchFailure( @@ -186,27 +186,27 @@ class LowerPacksAndUnPacks RewritePatternSet patterns(ctx); linalgx::utils::populateScfForToForAllRewritePattern(patterns); scf::ForallOp::getCanonicalizationPatterns(patterns, ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } // Step3. Simplify packs and unpacks. { RewritePatternSet patterns(ctx); mlir::tpp::populateSimplifyPacking(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } // Step4. Generalize to linalg. { RewritePatternSet patterns(ctx); patterns.add(ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } // Step5. Fallback on tile by one + generalization patterns. { IRRewriter rewriter(ctx); - getOperation()->walk([&](tensor::UnPackOp unPackOp) { + getOperation()->walk([&](linalg::UnPackOp unPackOp) { scf::SCFTilingOptions unpackTilingOptions; SmallVector tiles(unPackOp.getDestType().getRank(), 1); unpackTilingOptions.setTileSizes(getAsIndexOpFoldResult(ctx, tiles)); @@ -215,9 +215,9 @@ class LowerPacksAndUnPacks unpackTilingOptions); if (failed(tilingResult)) return signalPassFailure(); - rewriter.replaceOp(unPackOp, tilingResult->replacements); + rewriter.replaceOp(unPackOp, tilingResult->mergeResult.replacements); }); - getOperation()->walk([&](tensor::PackOp packOp) { + getOperation()->walk([&](linalg::PackOp packOp) { SmallVector tiles(packOp.getSourceType().getRank(), 1); scf::SCFTilingOptions packTilingOptions; packTilingOptions.setTileSizes(getAsIndexOpFoldResult(ctx, tiles)); @@ -226,14 +226,13 @@ class LowerPacksAndUnPacks packTilingOptions); if (failed(tilingResult)) return signalPassFailure(); - rewriter.replaceOp(packOp, tilingResult->replacements); + rewriter.replaceOp(packOp, tilingResult->mergeResult.replacements); }); RewritePatternSet patterns(&getContext()); patterns.add(&getContext()); tensor::populateMergeConsecutiveInsertExtractSlicePatterns(patterns); - if (failed(applyPatternsAndFoldGreedily(getOperation(), - std::move(patterns)))) { + if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) { return signalPassFailure(); } } @@ -246,7 +245,7 @@ class LowerPacksAndUnPacks ->getCanonicalizationPatterns(patterns); ctx->getLoadedDialect() ->getCanonicalizationPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } } }; diff --git a/lib/TPP/Transforms/LowerPacksAndUnpacksWithoutTranspose.cpp b/lib/TPP/Transforms/LowerPacksAndUnpacksWithoutTranspose.cpp index 57b2b6030..2ecd54d1c 100644 --- a/lib/TPP/Transforms/LowerPacksAndUnpacksWithoutTranspose.cpp +++ b/lib/TPP/Transforms/LowerPacksAndUnpacksWithoutTranspose.cpp @@ -39,10 +39,10 @@ namespace { /// Wrapper around linalg::lowerPack which undoes the transpose that might have /// happened. Single user genericOp's indexing_maps is corrected accordingly. -void lowerPackAndFoldTranspose(tensor::PackOp packOp, +void lowerPackAndFoldTranspose(linalg::PackOp packOp, linalg::GenericOp genericOp, uint operandIdx, PatternRewriter &rewriter) { - auto packInversionPerm = tensor::getPackInverseDestPerm(packOp); + auto packInversionPerm = linalg::getPackInverseDestPerm(packOp); auto res = linalg::lowerPack(rewriter, packOp); @@ -67,7 +67,7 @@ struct LowerPackOnInputsFoldingTranspose : public OpRewritePattern { // Is only called with single-user packOp operands, so callback can always // find the (use by the) linalg.generic that is the target of the pattern. - using ControlFn = std::function; + using ControlFn = std::function; ControlFn controlFn; LowerPackOnInputsFoldingTranspose(MLIRContext *context, @@ -81,7 +81,7 @@ struct LowerPackOnInputsFoldingTranspose for (auto &&[operandIdx, inOperand] : llvm::enumerate(genericOp.getInputs())) { auto packOp = - dyn_cast_if_present(inOperand.getDefiningOp()); + dyn_cast_if_present(inOperand.getDefiningOp()); if (!packOp || !packOp->hasOneUse() || (controlFn && !controlFn(packOp))) continue; @@ -99,7 +99,7 @@ struct LowerPackUnpackOnOutputFoldingTranspose : public OpRewritePattern { // Is only called with single-user packOp operands, so callback can always // find the (use by the) linalg.generic that is the target of the pattern. - using ControlFn = std::function; + using ControlFn = std::function; ControlFn controlFn; LowerPackUnpackOnOutputFoldingTranspose(MLIRContext *context, @@ -120,9 +120,9 @@ struct LowerPackUnpackOnOutputFoldingTranspose continue; auto packOp = - dyn_cast_if_present(outOperand.getDefiningOp()); + dyn_cast_if_present(outOperand.getDefiningOp()); auto unpackOp = - llvm::dyn_cast(*(result.getUsers().begin())); + llvm::dyn_cast(*(result.getUsers().begin())); if (!packOp || !packOp->hasOneUse() || !unpackOp) continue; @@ -188,14 +188,14 @@ struct LowerPacksAndUnpacksWithoutTranspose RewritePatternSet patterns(ctx); patterns.add( - ctx, [](tensor::PackOp packOp) { + ctx, [](linalg::PackOp packOp) { // Only lower packOps whose argument is not a constant. return !llvm::dyn_cast_if_present( packOp.getOperand(0).getDefiningOp()); }); patterns.add(ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/RewriteBatchMatmulToMatmul.cpp b/lib/TPP/Transforms/RewriteBatchMatmulToMatmul.cpp index 0dbfed330..5c9751220 100644 --- a/lib/TPP/Transforms/RewriteBatchMatmulToMatmul.cpp +++ b/lib/TPP/Transforms/RewriteBatchMatmulToMatmul.cpp @@ -111,7 +111,7 @@ struct RewriteBatchMatmulToMatmul tilingOpts); if (failed(tilingResult)) return signalPassFailure(); - rewriter.replaceOp(batchMatmulOp, tilingResult->replacements); + rewriter.replaceOp(batchMatmulOp, tilingResult->mergeResult.replacements); }); // Step2: @@ -125,8 +125,7 @@ struct RewriteBatchMatmulToMatmul patterns.getContext()); ctx.getOrLoadDialect()->getCanonicalizationPatterns( patterns); - if (failed(applyPatternsAndFoldGreedily(getOperation(), - std::move(patterns)))) { + if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) { return signalPassFailure(); } } diff --git a/lib/TPP/Transforms/RewriteConvsToMatmulOrBrgemm.cpp b/lib/TPP/Transforms/RewriteConvsToMatmulOrBrgemm.cpp index d00caaf99..dddd1c1f7 100644 --- a/lib/TPP/Transforms/RewriteConvsToMatmulOrBrgemm.cpp +++ b/lib/TPP/Transforms/RewriteConvsToMatmulOrBrgemm.cpp @@ -571,7 +571,7 @@ struct RewriteConvToMatmulOrBrgemm populateRewrite2DNhwcHwcfConvPatterns(patterns); populateRewriteBlockedConvPatterns(patterns, this->enableBrgemm); tensor::populateMergeConsecutiveInsertExtractSlicePatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/SplitReductionDim.cpp b/lib/TPP/Transforms/SplitReductionDim.cpp index 23bdcad39..ecc2e3e22 100644 --- a/lib/TPP/Transforms/SplitReductionDim.cpp +++ b/lib/TPP/Transforms/SplitReductionDim.cpp @@ -81,7 +81,7 @@ struct SplitContractionReduction return rewriter.notifyMatchFailure(linalgOp, "failed to tile contraction"); - rewriter.replaceOp(linalgOp, tilingResult->replacements); + rewriter.replaceOp(linalgOp, tilingResult->mergeResult.replacements); return success(); } @@ -104,8 +104,7 @@ struct SplitReductionDim patterns.add(ctx, options); GreedyRewriteConfig config; config.strictMode = GreedyRewriteStrictness::ExistingOps; - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns), - config); + (void)applyPatternsGreedily(getOperation(), std::move(patterns), config); } }; diff --git a/lib/TPP/Transforms/TileConsumerAndFuseProducers.cpp b/lib/TPP/Transforms/TileConsumerAndFuseProducers.cpp index 3f24c1bdd..9cc84ecb1 100644 --- a/lib/TPP/Transforms/TileConsumerAndFuseProducers.cpp +++ b/lib/TPP/Transforms/TileConsumerAndFuseProducers.cpp @@ -724,7 +724,7 @@ struct TileConsumerAndFuseProducers // Attempt to recover named ops. RewritePatternSet patterns(&ctx); linalg::populateLinalgDeGeneralizationPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } int64_t numIters = this->numIters; @@ -749,7 +749,7 @@ struct TileConsumerAndFuseProducers // TODO: Remove the generalization of named ops after resolving the // above dependency with "populateFoldUnitExtentDimsViaSlicesPatterns". linalg::populateLinalgNamedOpsGeneralizationPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } } while (--numIters); @@ -757,7 +757,7 @@ struct TileConsumerAndFuseProducers // Patterns for scf.for. RewritePatternSet patterns(&ctx); patterns.add(&ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } { @@ -765,7 +765,7 @@ struct TileConsumerAndFuseProducers RewritePatternSet patterns(&ctx); if (this->useForAll) linalgx::utils::populateScfForToForAllRewritePattern(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } { @@ -773,7 +773,7 @@ struct TileConsumerAndFuseProducers RewritePatternSet patterns(&ctx); linalg::populateLinalgDeGeneralizationPatterns(patterns); scf::ForallOp::getCanonicalizationPatterns(patterns, &ctx); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } } }; @@ -804,7 +804,7 @@ struct ElementWiseFusion : tpp::impl::ElementWiseFusionBase { linalg::populateElementwiseOpsFusionPatterns(patterns, fuseElementwiseOpsControlFn); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; diff --git a/lib/TPP/Transforms/ToBlockLayoutAndBack.cpp b/lib/TPP/Transforms/ToBlockLayoutAndBack.cpp index 688c492f6..a5fb6f68d 100644 --- a/lib/TPP/Transforms/ToBlockLayoutAndBack.cpp +++ b/lib/TPP/Transforms/ToBlockLayoutAndBack.cpp @@ -58,13 +58,13 @@ static Value toPackLayoutImpl(OpBuilder &builder, Location loc, Value input, SmallVector staticTiles; dispatchIndexOpFoldResults(tiles, dynamicTiles, staticTiles); RankedTensorType result = - tensor::PackOp::inferPackedType(cast(input.getType()), + linalg::PackOp::inferPackedType(cast(input.getType()), staticTiles, innerDimsPos, outerDimsPerm); auto inputType = cast(input.getType()); ArrayRef shape = result.getShape(); Value output = builder.create(loc, shape, inputType.getElementType()); - return builder.create(loc, input, output, innerDimsPos, tiles, + return builder.create(loc, input, output, innerDimsPos, tiles, /*paddingValue=*/std::nullopt, outerDimsPerm); } @@ -76,7 +76,7 @@ static Value toUnPackLayoutImpl(OpBuilder &builder, Location loc, Value input, ArrayRef outerDimsPerm) { if (auto fillOp = output.getDefiningOp()) output = fillOp.getOutputs()[0]; - return builder.create(loc, input, output, innerDimPos, + return builder.create(loc, input, output, innerDimPos, tiles, outerDimsPerm); } @@ -562,7 +562,7 @@ struct PackMatmul : public tpp::impl::PackMatmulBase { linalg::populateBlockPackMatmulPatterns(patterns, packControlFn); linalg::populateLinalgDeGeneralizationPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; @@ -598,7 +598,7 @@ struct PackConv2DNchwFchw MLIRContext *ctx = getOperation().getContext(); RewritePatternSet patterns(ctx); patterns.add(ctx, blockingFactors); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; @@ -634,7 +634,7 @@ struct PackConv2DNhwcHwcf MLIRContext *ctx = getOperation().getContext(); RewritePatternSet patterns(ctx); patterns.add(ctx, blockingFactors); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; @@ -674,8 +674,8 @@ struct PackVNNI : public tpp::impl::PackVNNIBase { RewritePatternSet patterns(ctx); linalg::populateLinalgDeGeneralizationPatterns(patterns); patterns.add(ctx); - tensor::populateSimplifyPackAndUnpackPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + linalg::populateSimplifyPackAndUnpackPatterns(patterns); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; @@ -686,29 +686,29 @@ struct PropagatePackUnPack RewritePatternSet patterns(ctx); linalg::populateDataLayoutPropagationPatterns( patterns, [](OpOperand *operand) { return true; }); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; -// Fold a tensor.unpack into an scf.parallel_insert. +// Fold a linalg.unpack into an scf.parallel_insert. // // The pattern looks like: // -// %p = tensor.pack %a into %b +// %p = linalg.pack %a into %b // %l = scf.forall ... iter_args(%0 = %p) { // ... // } -// %u = tensor.unpack %l into %c +// %u = linalg.unpack %l into %c // // We will rewrite as: // // %l = scf.forall ... iter_args(%0 = %a) { // ... // } -struct FoldUnPackIntoInsertSlice : public OpRewritePattern { - using OpRewritePattern::OpRewritePattern; +struct FoldUnPackIntoInsertSlice : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(tensor::UnPackOp unPackOp, + LogicalResult matchAndRewrite(linalg::UnPackOp unPackOp, PatternRewriter &rewriter) const override { if (!unPackOp.getOuterDimsPerm().empty()) return failure(); @@ -731,8 +731,8 @@ struct FoldUnPackIntoInsertSlice : public OpRewritePattern { // Create a new scf.forall operation, updating its output. Value loopOperand = forallOp.getTiedOpOperand(forallOp->getResult(0))->get(); - tensor::PackOp packOp = - dyn_cast_or_null(loopOperand.getDefiningOp()); + linalg::PackOp packOp = + dyn_cast_or_null(loopOperand.getDefiningOp()); if (!packOp) return failure(); Value newLoopOperand = packOp.getSource(); @@ -823,7 +823,7 @@ struct SimplifyAndCanonicalizePack MLIRContext *ctx = getOperation().getContext(); RewritePatternSet patterns(ctx); tpp::populateSimplifyPacking(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; @@ -831,10 +831,11 @@ struct SimplifyAndCanonicalizePack void mlir::tpp::populateSimplifyPacking(RewritePatternSet &patterns) { MLIRContext *ctx = patterns.getContext(); - tensor::populateSimplifyPackAndUnpackPatterns(patterns); + linalg::populateSimplifyPackAndUnpackPatterns(patterns); + linalg::populateFoldPackUnpackIntoTensorEmptyPatterns(patterns); tensor::populateFoldTensorEmptyPatterns(patterns); - tensor::PackOp::getCanonicalizationPatterns(patterns, ctx); - tensor::UnPackOp::getCanonicalizationPatterns(patterns, ctx); + linalg::PackOp::getCanonicalizationPatterns(patterns, ctx); + linalg::UnPackOp::getCanonicalizationPatterns(patterns, ctx); tensor::ExtractSliceOp::getCanonicalizationPatterns(patterns, ctx); tensor::CollapseShapeOp::getCanonicalizationPatterns(patterns, ctx); tensor::CastOp::getCanonicalizationPatterns(patterns, ctx); @@ -849,6 +850,8 @@ void mlir::tpp::populateSimplifyPacking(RewritePatternSet &patterns) { patterns, [](OpOperand *operand) { return isa(operand->get().getDefiningOp()); }); + ctx->getLoadedDialect()->getCanonicalizationPatterns( + patterns); ctx->getLoadedDialect()->getCanonicalizationPatterns( patterns); patterns.add(ctx); diff --git a/lib/TPP/Transforms/TransformUtils.cpp b/lib/TPP/Transforms/TransformUtils.cpp index 8e3a75b10..2ee0b7b9b 100644 --- a/lib/TPP/Transforms/TransformUtils.cpp +++ b/lib/TPP/Transforms/TransformUtils.cpp @@ -138,14 +138,16 @@ Value getSliceOperand(OpBuilder &builder, linalg::LinalgOp linalgOp, assert(rank == strides.size() && "expect rank == strides"); Location loc = linalgOp.getLoc(); - Type reducedType = - (linalgOp.hasPureTensorSemantics()) - ? tensor::ExtractSliceOp::inferCanonicalRankReducedResultType( - desiredResultRank, cast(operandType), offsets, - sizes, strides) - : memref::SubViewOp::inferRankReducedResultType( - getExpectedResultMemRefShape(sizes, desiredResultRank), - cast(operandType), offsets, sizes, strides); + Type reducedType; + if (linalgOp.hasPureTensorSemantics()) { + reducedType = tensor::ExtractSliceOp::inferCanonicalRankReducedResultType( + desiredResultRank, cast(operandType), offsets, sizes, + strides); + } else { + reducedType = memref::SubViewOp::inferRankReducedResultType( + getExpectedResultMemRefShape(sizes, desiredResultRank), + cast(operandType), offsets, sizes, strides); + } Operation *extractOperation = (linalgOp.hasPureTensorSemantics()) diff --git a/lib/TPP/Transforms/Utils/ValueUtils.cpp b/lib/TPP/Transforms/Utils/ValueUtils.cpp index 27a453a59..154ef6b26 100644 --- a/lib/TPP/Transforms/Utils/ValueUtils.cpp +++ b/lib/TPP/Transforms/Utils/ValueUtils.cpp @@ -102,7 +102,7 @@ FailureOr> getStaticStrides(MemRefType valueType) { auto memrefType = cast(valueType); SmallVector strides; int64_t offset; - if (failed(getStridesAndOffset(memrefType, strides, offset))) { + if (failed(memrefType.getStridesAndOffset(strides, offset))) { return failure(); } if (llvm::any_of(strides, [](int64_t stride) { diff --git a/lib/TPP/Transforms/VectorContractToFMA.cpp b/lib/TPP/Transforms/VectorContractToFMA.cpp index 78407797b..6bb274df8 100644 --- a/lib/TPP/Transforms/VectorContractToFMA.cpp +++ b/lib/TPP/Transforms/VectorContractToFMA.cpp @@ -381,7 +381,7 @@ void VectorContractToFMA::runOnOperation() { RewritePatternSet patterns(context); patterns.add(context, ctx); - if (failed(applyPatternsAndFoldGreedily(funcOp, std::move(patterns)))) { + if (failed(applyPatternsGreedily(funcOp, std::move(patterns)))) { signalPassFailure(); } } @@ -391,4 +391,4 @@ void VectorContractToFMA::runOnOperation() { std::unique_ptr createVectorContractToFMA() { return std::make_unique(); -} \ No newline at end of file +} diff --git a/lib/TPP/Transforms/VectorContractToOuterproduct.cpp b/lib/TPP/Transforms/VectorContractToOuterproduct.cpp index 04c8c156b..858e8a261 100644 --- a/lib/TPP/Transforms/VectorContractToOuterproduct.cpp +++ b/lib/TPP/Transforms/VectorContractToOuterproduct.cpp @@ -273,7 +273,7 @@ struct VectorContractToOuterproduct RewritePatternSet patterns(context); patterns.add(context); - if (failed(applyPatternsAndFoldGreedily(funcOp, std::move(patterns)))) { + if (failed(applyPatternsGreedily(funcOp, std::move(patterns)))) { signalPassFailure(); } } diff --git a/lib/TPP/Transforms/Vectorization.cpp b/lib/TPP/Transforms/Vectorization.cpp index 38406f0f9..47d14916f 100644 --- a/lib/TPP/Transforms/Vectorization.cpp +++ b/lib/TPP/Transforms/Vectorization.cpp @@ -117,7 +117,7 @@ struct VectorizationPass populateCombinePatterns(patterns); vector::populateVectorTransferPermutationMapLoweringPatterns(patterns); vector::populateVectorReductionToContractPatterns(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } }; } // namespace tpp diff --git a/test/BF16/Integration/vnni-packing-chain.mlir b/test/BF16/Integration/vnni-packing-chain.mlir index 45c6e65be..8bb3e6b7f 100644 --- a/test/BF16/Integration/vnni-packing-chain.mlir +++ b/test/BF16/Integration/vnni-packing-chain.mlir @@ -6,10 +6,10 @@ func.func @vnni_packing(%arg0: tensor<32x32xbf16>, %arg1: tensor<2x2x8x16x2xbf16>) -> tensor<2x2x8x16x2xbf16> { %0 = tensor.empty() : tensor<2x2x16x16xbf16> - %pack = tensor.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [16, 16] + %pack = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [16, 16] into %0 : tensor<32x32xbf16> -> tensor<2x2x16x16xbf16> // IR: xsmm_unary_invoke - %vnni_pack = tensor.pack %pack inner_dims_pos = [2] inner_tiles = [2] + %vnni_pack = linalg.pack %pack inner_dims_pos = [2] inner_tiles = [2] into %arg1 : tensor<2x2x16x16xbf16> -> tensor<2x2x8x16x2xbf16> return %vnni_pack : tensor<2x2x8x16x2xbf16> } diff --git a/test/BF16/Integration/vnni-packing.mlir b/test/BF16/Integration/vnni-packing.mlir index d1f19887a..a694e0c48 100644 --- a/test/BF16/Integration/vnni-packing.mlir +++ b/test/BF16/Integration/vnni-packing.mlir @@ -3,7 +3,7 @@ // RUN: FileCheck %s func.func @vnni_packing(%arg0: tensor<16x16xbf16>, %arg1: tensor<8x16x2xbf16>) -> tensor<8x16x2xbf16> { - %pack = tensor.pack %arg0 inner_dims_pos = [0] inner_tiles = [2] into %arg1 : tensor<16x16xbf16> -> tensor<8x16x2xbf16> + %pack = linalg.pack %arg0 inner_dims_pos = [0] inner_tiles = [2] into %arg1 : tensor<16x16xbf16> -> tensor<8x16x2xbf16> return %pack : tensor<8x16x2xbf16> } diff --git a/test/BF16/brgemm-tpp.mlir b/test/BF16/brgemm-tpp.mlir index 7ab922e62..857541bff 100644 --- a/test/BF16/brgemm-tpp.mlir +++ b/test/BF16/brgemm-tpp.mlir @@ -16,7 +16,7 @@ func.func @brgemm(%arg0: tensor<32x4x4xbf16>, %arg1: tensor<32x4x4xbf16>, // CHECK-SAME: %[[ARG2:.+]]: tensor<4x4xbf16> // CHECK: %[[VNNI_A:.+]] = tensor.expand_shape %[[ARG0]] {{\[}}[0], [1], [2, 3]] output_shape{{.*}}: tensor<32x4x4xbf16> into tensor<32x4x{{2|1}}x{{2|4}}xbf16> // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<32x{{2|1}}x4x{{2|4}}xbf16> -// CHECK: %[[PACK:.+]] = tensor.pack %[[ARG1]] inner_dims_pos = [1] inner_tiles = [{{2|4}}] +// CHECK: %[[PACK:.+]] = linalg.pack %[[ARG1]] inner_dims_pos = [1] inner_tiles = [{{2|4}}] // CHECK-SAME: into %[[EMPTY]] : tensor<32x4x4xbf16> -> tensor<32x{{2|1}}x4x{{2|4}}xbf16> // CHECK: %{{.+}} = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP]], #[[MAP1]], #[[MAP2]]] diff --git a/test/BF16/brgemm-vnni.mlir b/test/BF16/brgemm-vnni.mlir index 5970ebec4..309e563bd 100644 --- a/test/BF16/brgemm-vnni.mlir +++ b/test/BF16/brgemm-vnni.mlir @@ -16,7 +16,7 @@ func.func @brgemm(%arg0: tensor<32x4x4xbf16>, %arg1: tensor<32x4x4xbf16>, // CHECK-SAME: %[[ARG2:.+]]: tensor<4x4xbf16> // CHECK: %[[VNNI_A:.+]] = tensor.expand_shape %[[ARG0]] {{\[}}[0], [1], [2, 3]] output_shape{{.*}}: tensor<32x4x4xbf16> into tensor<32x4x{{2|1}}x{{2|4}}xbf16> // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<32x{{2|1}}x4x{{2|4}}xbf16> -// CHECK: %[[PACK:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: inner_dims_pos = [1] inner_tiles = [{{2|4}}] into %[[EMPTY]] // CHECK-SAME: : tensor<32x4x4xbf16> -> tensor<32x{{2|1}}x4x{{2|4}}xbf16> // CHECK: linalg.generic @@ -71,7 +71,7 @@ func.func @prepacked_matmul(%pack: tensor<4x4x32x32xbf16>, %pack_0: tensor<4x4x3 // CHECK: %[[VNNI_A:.+]] = tensor.expand_shape %[[ARG0]] {{\[}}[0], [1], [2], [3, 4]] // CHECK-SAME: output_shape{{.*}}: tensor<4x4x32x32xbf16> into tensor<4x4x32x{{16|8}}x{{2|4}}xbf16> // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<4x4x{{16|8}}x32x{{2|4}}xbf16> -// CHECK: %[[PACK:.+]] = tensor.pack %[[ARG1]] inner_dims_pos = [2] inner_tiles = [{{2|4}}] into %[[EMPTY]] +// CHECK: %[[PACK:.+]] = linalg.pack %[[ARG1]] inner_dims_pos = [2] inner_tiles = [{{2|4}}] into %[[EMPTY]] // CHECK-SAME: : tensor<4x4x32x32xbf16> -> tensor<4x4x{{16|8}}x32x{{2|4}}xbf16> // CHECK: {{.+}} = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP]], #[[MAP1]], #[[MAP2]]] @@ -102,7 +102,7 @@ func.func @already_packed_matmul(%arg0: tensor<4x4x32x16x2xbf16>, %arg1: tensor< // CHECK-LABEL: already_packed_matmul // CHECK-NOT: expand_shape -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: linalg.generic // ----- @@ -128,7 +128,7 @@ func.func @no_pack_invalid_reduction_map(%arg0: tensor<3x16x8xbf16>, // CHECK: no_pack_invalid_reduction_map // CHECK-NOT: expand_shape -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: linalg.generic // ----- @@ -154,5 +154,5 @@ func.func @no_pack_not_contraction(%arg0: tensor<4x4x32x32xbf16>, %arg1: tensor< // CHECK: no_pack_not_contraction // CHECK-NOT: expand_shape -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: linalg.generic diff --git a/test/BF16/matmul-tiled-vnni.mlir b/test/BF16/matmul-tiled-vnni.mlir index 342ef35b9..ddf3aefc0 100644 --- a/test/BF16/matmul-tiled-vnni.mlir +++ b/test/BF16/matmul-tiled-vnni.mlir @@ -48,7 +48,7 @@ module { // CHECK: %{{.+}}: tensor<32x128x4x4xbf16>) -> tensor<32x128x4x4xbf16> { // CHECK: scf.for // CHECK: scf.for -// CHECK: %{{.+}} = tensor.pack +// CHECK: %{{.+}} = linalg.pack // CHECK: %{{.+}} = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP]], #[[MAP1]], #[[MAP2]]] // CHECK-SAME: iterator_types = ["reduction", "parallel", "parallel", "reduction", "reduction"] diff --git a/test/BF16/matmul-vnni.mlir b/test/BF16/matmul-vnni.mlir index 24e83a8b3..bdc77d318 100644 --- a/test/BF16/matmul-vnni.mlir +++ b/test/BF16/matmul-vnni.mlir @@ -19,15 +19,15 @@ func.func @matmul_static( // CHECK-LABEL: matmul_static // CHECK-SAME: %[[ARG0:.+]]: tensor<256x512xbf16>, %[[ARG1:.+]]: tensor<512x1024xbf16>, %[[ARG2:.+]]: tensor<256x1024xbf16> // CHECK: %[[EMPTY_0:.+]] = tensor.empty() : tensor<8x16x32x32xbf16> -// CHECK: %[[PACK:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] +// CHECK: %[[PACK:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[EMPTY_0]] : tensor<256x512xbf16> -> tensor<8x16x32x32xbf16> // CHECK: %[[EMPTY_1:.+]] = tensor.empty() : tensor<32x16x32x32xbf16> -// CHECK: %[[PACK_0:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] +// CHECK: %[[PACK_0:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %{{.+}} : tensor<512x1024xbf16> -> tensor<32x16x32x32xbf16> // CHECK: %[[VNNI_A:.+]] = tensor.expand_shape %[[PACK]] {{\[}}[0], [1], [2], [3, 4]] // CHECK-SAME: output_shape{{.*}}: tensor<8x16x32x32xbf16> into tensor<8x16x32x{{16|8}}x{{2|4}}xbf16> // CHECK: %[[EMPTY_2:.+]] = tensor.empty() : tensor<32x16x{{16|8}}x32x{{2|4}}xbf16> -// CHECK: %[[PACK_1:.+]] = tensor.pack %[[PACK_0]] inner_dims_pos = [2] inner_tiles = [{{2|4}}] into %[[EMPTY_2]] +// CHECK: %[[PACK_1:.+]] = linalg.pack %[[PACK_0]] inner_dims_pos = [2] inner_tiles = [{{2|4}}] into %[[EMPTY_2]] // CHECK-SAME: : tensor<32x16x32x32xbf16> -> tensor<32x16x{{16|8}}x32x{{2|4}}xbf16> // CHECK: %{{.+}} = scf.forall (%[[ARG3:.+]], %[[ARG4:.+]]) in (8, 32) shared_outs(%[[ARG5:.+]] = %[[ARG2]]) // CHECK: %[[APPLY:.+]] = affine.apply #[[MAP]](%[[ARG3]]) diff --git a/test/GPU/CUDA/Integration/gpu-printf.mlir b/test/GPU/CUDA/Integration/gpu-printf.mlir index 3002aaa09..9373a4a7f 100644 --- a/test/GPU/CUDA/Integration/gpu-printf.mlir +++ b/test/GPU/CUDA/Integration/gpu-printf.mlir @@ -9,7 +9,7 @@ module attributes {gpu.container_module} { %0 = gpu.thread_id x %csti8 = arith.constant 2 : i8 %cstf32 = arith.constant 3.0 : f32 - gpu.printf "Hello from %lld, %d, %f\n" %0, %csti8, %cstf32 : index, i8, f32 + gpu.printf "Hello from %lld, %d, %f\n", %0, %csti8, %cstf32 : index, i8, f32 gpu.return } } diff --git a/test/GPU/set-spirv-abi-attr.mlir b/test/GPU/set-spirv-abi-attr.mlir index 6496bb48e..89f24cc65 100644 --- a/test/GPU/set-spirv-abi-attr.mlir +++ b/test/GPU/set-spirv-abi-attr.mlir @@ -17,7 +17,7 @@ module attributes {gpu.container_module} { %b1 = gpu.block_id y %t0 = gpu.thread_id x %t1 = gpu.thread_id y - gpu.printf "Block (%lld, %lld, 1) - Thread (%lld, %lld, 1)\n" %b0, %b1, %t0, %t1 : index, index, index, index + gpu.printf "Block (%lld, %lld, 1) - Thread (%lld, %lld, 1)\n", %b0, %b1, %t0, %t1 : index, index, index, index gpu.return } } diff --git a/test/Integration/pack-unpack-conversion.mlir b/test/Integration/pack-unpack-conversion.mlir index 6e420e28f..36c797a49 100644 --- a/test/Integration/pack-unpack-conversion.mlir +++ b/test/Integration/pack-unpack-conversion.mlir @@ -56,7 +56,7 @@ func.func @entry() { // %unpacked_tensor = bufferization.alloc_tensor() : tensor<13x15xf32> - %unpack = tensor.unpack %input_tensor_bcast inner_dims_pos = [0, 1] inner_tiles = [8, 2] + %unpack = linalg.unpack %input_tensor_bcast inner_dims_pos = [0, 1] inner_tiles = [8, 2] into %unpacked_tensor : tensor<2x8x8x2xf32> -> tensor<13x15xf32> %v0 = vector.transfer_read %unpack[%c0, %c0], %d1 : tensor<13x15xf32>, vector<13x15xf32> @@ -102,7 +102,7 @@ func.func @entry() { // %unpacked_tensor1 = bufferization.alloc_tensor() : tensor<1x1x32x8xf32> - %unpack1 = tensor.unpack %input_tensor_bcast1 inner_dims_pos = [3, 2] inner_tiles = [8, 32] + %unpack1 = linalg.unpack %input_tensor_bcast1 inner_dims_pos = [3, 2] inner_tiles = [8, 32] into %unpacked_tensor1 : tensor<1x1x1x1x8x32xf32> -> tensor<1x1x32x8xf32> %v3 = vector.transfer_read %unpack1[%c0, %c0, %c0, %c0], %d1 : tensor<1x1x32x8xf32>, vector<1x1x32x8xf32> @@ -151,7 +151,7 @@ func.func @entry() { outs(%bcast2: tensor<1x4x6x6x2xf32>) dimensions = [0, 1, 2, 3] %unpacked_tensor2 = bufferization.alloc_tensor() : tensor<1x6x6x8xf32> - %unpack2 = tensor.unpack %input_tensor_bcast2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] + %unpack2 = linalg.unpack %input_tensor_bcast2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] into %unpacked_tensor2 : tensor<1x4x6x6x2xf32> -> tensor<1x6x6x8xf32> %v4 = vector.transfer_read %unpack2[%c0, %c0, %c0, %c0], %d1 : tensor<1x6x6x8xf32>, vector<1x6x6x8xf32> vector.print %v4 : vector<1x6x6x8xf32> @@ -203,7 +203,7 @@ func.func @entry() { outs(%bcast3: tensor<1x6x6x8xf32>) dimensions = [0, 1, 2] %packed_tensor = bufferization.alloc_tensor() : tensor<1x4x6x6x2xf32> - %pack = tensor.pack %input_tensor_bcast3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] + %pack = linalg.pack %input_tensor_bcast3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] into %packed_tensor : tensor<1x6x6x8xf32> -> tensor<1x4x6x6x2xf32> %v5 = vector.transfer_read %pack[%c0, %c0, %c0, %c0, %c0], %d1 : tensor<1x4x6x6x2xf32>, vector<1x4x6x6x2xf32> @@ -241,7 +241,7 @@ func.func @entry() { outs(%bcast4: tensor<1x1x32x8xf32>) dimensions = [0, 1, 2] %packed_tensor1 = bufferization.alloc_tensor() : tensor<1x1x1x1x8x32xf32> - %pack1 = tensor.pack %input_tensor_bcast4 inner_dims_pos = [3, 2] inner_tiles = [8, 32] + %pack1 = linalg.pack %input_tensor_bcast4 inner_dims_pos = [3, 2] inner_tiles = [8, 32] into %packed_tensor1 : tensor<1x1x32x8xf32> -> tensor<1x1x1x1x8x32xf32> %v6 = vector.transfer_read %pack1[%c0, %c0, %c0, %c0, %c0, %c0], %d1 : tensor<1x1x1x1x8x32xf32>, vector<1x1x1x1x8x32xf32> diff --git a/test/Integration/tpp-pack-unpack.mlir b/test/Integration/tpp-pack-unpack.mlir index 2a59e3809..ad2a4f4ae 100644 --- a/test/Integration/tpp-pack-unpack.mlir +++ b/test/Integration/tpp-pack-unpack.mlir @@ -1,27 +1,27 @@ // RUN: tpp-run %s -e entry -entry-point-result=void | FileCheck %s func.func private @pack1(%in: tensor<4x4xf32>, %out: tensor<2x2x2x2xf32>) -> tensor<2x2x2x2xf32> { - %1 = tensor.pack %in inner_dims_pos = [0, 1] inner_tiles = [2,2] into %out : tensor<4x4xf32> -> tensor<2x2x2x2xf32> + %1 = linalg.pack %in inner_dims_pos = [0, 1] inner_tiles = [2,2] into %out : tensor<4x4xf32> -> tensor<2x2x2x2xf32> return %1 : tensor<2x2x2x2xf32> } func.func private @pack2(%0: tensor<1x2x2x4xf32>, %1: tensor<1x2x2x2x2xf32>)-> tensor<1x2x2x2x2xf32>{ - %2 = tensor.pack %0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] into %1 : tensor<1x2x2x4xf32> -> tensor<1x2x2x2x2xf32> + %2 = linalg.pack %0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] into %1 : tensor<1x2x2x4xf32> -> tensor<1x2x2x2x2xf32> return %2: tensor<1x2x2x2x2xf32> } func.func private @pack3(%in: tensor<8x2x2x2xf32>, %out: tensor<2x2x1x4x2x2xf32>)-> tensor<2x2x1x4x2x2xf32>{ - %2 = tensor.pack %in outer_dims_perm = [3, 2, 1, 0] inner_dims_pos=[1, 0] inner_tiles = [2, 2] into %out: tensor<8x2x2x2xf32>->tensor<2x2x1x4x2x2xf32> + %2 = linalg.pack %in outer_dims_perm = [3, 2, 1, 0] inner_dims_pos=[1, 0] inner_tiles = [2, 2] into %out: tensor<8x2x2x2xf32>->tensor<2x2x1x4x2x2xf32> return %2: tensor<2x2x1x4x2x2xf32> } func.func private @unpack1(%in:tensor<2x2x2x2xf32>, %out: tensor<4x4xf32>) -> tensor<4x4xf32> { - %1 = tensor.unpack %in inner_dims_pos = [0, 1] inner_tiles = [2,2] into %out : tensor<2x2x2x2xf32> -> tensor<4x4xf32> + %1 = linalg.unpack %in inner_dims_pos = [0, 1] inner_tiles = [2,2] into %out : tensor<2x2x2x2xf32> -> tensor<4x4xf32> return %1 : tensor<4x4xf32> } func.func private @unpack2(%0: tensor<1x2x2x2x2xf32>, %1: tensor<1x2x2x4xf32>)-> tensor<1x2x2x4xf32>{ - %2 = tensor.unpack %0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] into %1 : tensor<1x2x2x2x2xf32>->tensor<1x2x2x4xf32> + %2 = linalg.unpack %0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] into %1 : tensor<1x2x2x2x2xf32>->tensor<1x2x2x4xf32> return %2: tensor<1x2x2x4xf32> } diff --git a/test/Passes/DefaultPipeline/default-pipeline.mlir b/test/Passes/DefaultPipeline/default-pipeline.mlir index fd8118a86..18e300f22 100644 --- a/test/Passes/DefaultPipeline/default-pipeline.mlir +++ b/test/Passes/DefaultPipeline/default-pipeline.mlir @@ -9,8 +9,6 @@ func.func @matmul(%A: tensor<4x8xf32>, // CHECK: llvm.func @xsmm_gemm_invoke // CHECK: llvm.func @xsmm_gemm_dispatch // CHECK: llvm.func @matmul(%[[ARG0:.+]]: !llvm.ptr, -// CHECK: llvm.insertvalue -// CHECK: llvm.mlir.constant // CHECK: llvm.call @xsmm_gemm_dispatch // CHECK: llvm.call @xsmm_gemm_invoke // CHECK: llvm.return diff --git a/test/Passes/DefaultPipeline/vnni.mlir b/test/Passes/DefaultPipeline/vnni.mlir index 7cebef489..cd6f4c274 100644 --- a/test/Passes/DefaultPipeline/vnni.mlir +++ b/test/Passes/DefaultPipeline/vnni.mlir @@ -114,7 +114,7 @@ module attributes { func.func @brgemm_static_tensor(%arg0: tensor<4x256x512xbf16>, %arg1: tensor<4x512x1024xbf16>, %arg2: tensor<256x1024xbf16>) -> tensor<256x1024xbf16> { // CHECK: %[[alloc:.*]] = memref.alloc{{.*}}: memref<4x256x1024x2xbf16> %0 = tensor.empty() : tensor<4x256x1024x2xbf16> - %1 = tensor.pack %arg1 inner_dims_pos = [1] inner_tiles = [2] into %0 : tensor<4x512x1024xbf16> -> tensor<4x256x1024x2xbf16> + %1 = linalg.pack %arg1 inner_dims_pos = [1] inner_tiles = [2] into %0 : tensor<4x512x1024xbf16> -> tensor<4x256x1024x2xbf16> // CHECK: call @xsmm_brgemm_dispatch // CHECK: %[[ptr0:.*]] = memref.extract_aligned_pointer_as_index %[[ARG0]] diff --git a/test/Passes/fold-pack-and-constant.mlir b/test/Passes/fold-pack-and-constant.mlir index 9b3ef6716..6bb8b5143 100644 --- a/test/Passes/fold-pack-and-constant.mlir +++ b/test/Passes/fold-pack-and-constant.mlir @@ -3,7 +3,7 @@ func.func @expect_to_fold_cst() -> tensor<8x2x1x1x32x32xi64> { %cst = arith.constant dense<1> : tensor<1x1x64x256xi64> %0 = tensor.empty() : tensor<8x2x1x1x32x32xi64> - %pack = tensor.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %0 : tensor<1x1x64x256xi64> -> tensor<8x2x1x1x32x32xi64> + %pack = linalg.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %0 : tensor<1x1x64x256xi64> -> tensor<8x2x1x1x32x32xi64> return %pack : tensor<8x2x1x1x32x32xi64> } @@ -18,7 +18,7 @@ func.func @expect_to_fold_fill() -> tensor<1x8x56x56x32xi64> { %0 = tensor.empty() : tensor<1x56x56x256xi64> %1 = linalg.fill ins(%c0_i64 : i64) outs(%0 : tensor<1x56x56x256xi64>) -> tensor<1x56x56x256xi64> %2 = tensor.empty() : tensor<1x8x56x56x32xi64> - %3 = tensor.pack %1 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x256xi64> -> tensor<1x8x56x56x32xi64> + %3 = linalg.pack %1 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x256xi64> -> tensor<1x8x56x56x32xi64> return %3 : tensor<1x8x56x56x32xi64> } diff --git a/test/Passes/fold-pack-chains.mlir b/test/Passes/fold-pack-chains.mlir index 128e63f37..37bb17c3f 100644 --- a/test/Passes/fold-pack-chains.mlir +++ b/test/Passes/fold-pack-chains.mlir @@ -9,13 +9,13 @@ func.func @chained_constant_packs(%arg0: tensor<64x64xbf16>) -> tensor<64x64xbf1 %cst_0 = arith.constant dense<"0x0000CA3B0000213E000000000000DA3D0000583E000000000000113D3C3EA43D373DA33D000000000000203ED23D563E0000B53E0000BF3C00000000C73D000000000000AB3CDF3B0000923E1B3E173E0000000000000000393EC93E00000000000000000000893E000000000000BF3D0000000000000000000000008A3E7B3E0000993D0000173E00004D3E000000000000000000000000CC3D283E533BA13ECF3D603E00000000E63D063E0000C53C3A3D0000FD3D000000000F3D413E00000000193E000000007D3E00000000443E000000000000C03D00000F3E00000000503E00000000C13C00000000000000000000DB3DB43DF13C000000008B3D253E0000303E4F3E1A3E0000EA3D00000000CC3D00000000603E0000553DFE3E773E0000B13E00000000673D000000000000000000005A3D00000000543EC43DA23D000000000000803E0000F43D00000B3E000000000000AA3D000000000000093E893E0000293EC43E0C3E000000000000AB3E1A3D463E033EEB3C00000000663E000000002A3E00006D3E423E7A3D2F3E0000743D7F3C00000000953EFC3E0000000000000000393D1D3D0000F83C0000823EA03DCD3E0000C63D153E883B4D3E2F3D833EA63D873E2B3D113E000000000000883D6A3E0000983E00000000643E323E00006A3DE83D363E6E3EEA3D000000000000003F5F3C00000A3DA83D923E0000DF3D803D00000000933DD53D963C0000403EAE3D1D3C00003B3B00000000013E000000001F3E00000000593E0000000000008F3B573E00000000793C073D413D0000000000000000000000000000D23D0000E13D2F3E00008F3D763C0000433E00000000973C00000000063E00005E3EE53C0000813E8E3E4B3D000000000000000000000000AA3E373D543D9E3EFF3D00002A3C00000000A33E273E0000493E000000000000043E000000008D3D000000000000423EA63D000000000000000000004B3E573E00000000A13E853E4F3E0000833BCD3E0000723D000000000000863D673E4D3E00000000613E863D0000893E00000000BD3EA63B713E9D3D893E0000A43EA13E0000373E803E0000000000009E3EE23D893E00000000D23D353D00006D3EA83DEC3D753BA63EA13ED43D00006F3C5C3E573E00000000D23E00000000EC3C853DB63E0000993D0000E03D803D213C383ED43D000000000000013F00000D3E0000000000000000533E000000001C3D9F3D00004F3E00000000000000000000C33AF73D000000000000173D00000000673E9C3E000000000000623D000000002A3E013E1F3D00000000BB3E0000F13D953B0000000000004D3D000000000000A83D0000A23EDD3DA23D0000E13DD83D143D00000000413EE73D0000773D0000BF3D0000163E000000000000000000000000C23C000000001F3C00000000063D7B3E000000009D3E00000000D13D0000923D00001E3E0000633CB93D0000D53BE13E293E00006A3E483E000000007C3EB43E103E0000DD3C00000000DD3D00000000903E00000000823EB23D00000000AB3D663E0000143E9F3D000000000000233E00000000F53D00007B3E0000000000008B3E1C3E00007A3DB13E5C3D533E0000D23C0000493E8B3E513E0000000000005F3D993DD53D0000D03E0000303E0000C63E0000A63E00008A3E00000000933E00000000EE3C933DA13EEA3C893E093E0000000000000000BB3D000000000000913B873E000000001C3E0000000000000000583D00000000000000006F3D1A3C793E793E00004B3E00003F3C0000A63E00001D3EFC3C0000B33D803E603EFD3D00004D3D933D00005B3C0000F83D1F3C313B613E00000000BA3D863D000000000000000000004D3EED3DFF3C0000CD3CDE3E00000000000000000000293ED33D1F3E013E0000383E0000E83C00009A3E863D0000553E3D3E773E983E0E3E803E4B3C563E0000763D683E883DB43DC73D0000000000000000C83D4F3D413EF03C00000000DB3D0000B03E0000C93D0000000000000000000000006E3E123E0000F53D00000000833D953E00000000000000000000033E0000DC3D00000000E53ECA3B00000000403C00000000D63C323C593EB63D000000004C3ED33D9A3E000000006D3E0000E63E173E0000103EE13D423E8C3D8E3D00000000DC3D0000000000000000493E00000000000000000000793ED23D00000000000000000000013E0000BD3C0000073E8C3D00000000E03EAE3D00003C3E000000000000503E00000000573D00000000D13EEC3C00000000000000004E3EF13D000000002C3E0000663AD93E0000023EEA3DB63E0000CD3D0000B93E593E3A3E913D1A3CD73DAF3E00000000E43D5A3D233D2A3E883E0000543E000000000000703E00000000A53C000000008D3E933DB13D000000000000FF3DA53EC83E000000005C3E00009A3B023E0000D73D863E0000943E00009B3CBF3D00000000103D0000523EBE3DFA3D0000D13D000000008F3D0000AA3DE53EA33CD83D533CA23D023E0000C83D0000000000000000883ED73E0F3E093C000000009A3E0000000000000000E73D00000000083C833C0000553DDE3C00000000BA3D00005D3E00009D3D003E0000683E4D3D923E1C3E5B3C0000133E0000BF3D0000833C823D0000703C000000001A3D000000000000A03EB23CA23E743E663E0000153E803CE83C073F833E953DC33EDA3D000000003B3E0000463D00000000D93D083D0000E83D0000000000000000B33D00000000C83D0000273E00000000193E163E000000004E3D00001D3D00007C3DA03E0000000000000F3E863E0F3E983E0000A43DBB3CC53E693D0000A73DC33D993EE63D0000000000004D3D1D3E263E0000E53D263E373D0000000000009F3E0000DA3B000000000F3E833E000000000000000000000000413D00000000D23C943E313DE23C243D000000003A3ED53D00000000000000000000323E0000AF3E000000000000783ED53D803E00000000A83CD63D00003D3EAE3E6F3E0000333E1C3E0000C63E00009F3E0000993E00000000463EC73E453D0000463EB43B000000000000D53E0000923D5E3E173ED83DBF3E00000000DE3E403D0000893E000000000000063E0000C23DBC3DF93D0000413D000000000000913DA43D00000000923DB03E523C9B3C00000000000000000C3EA83EA63D000000004B3E0000453D0000AB3D0000013F00005F3B2F3B00000000913C00000000363E173E00007E3E00006B3E00000000E73D00000000623E353BD03C8C3C0000000000000000173E1B3F000000004D3E573E0000000000000000863C0000000000004B3D0000263E000000008B3D673E993E883E00007D3E00000000873ED43DB43E000000008C3E0000963E000000000000000000000000C13DA23E00004A3E0000493DE23D5E3D4A3E723E00000000000000000000013F8A3E00007D3E533D000000000000963E00000000143D6F3E6A3C253C00000000A83E943E00000B3D273E0000AA3E023DC13E163D0000A63D0000203E963D00000000DF3A3A3E0000000000000000D23D0000000000000000833E373E713E0000F63C733E00000000BB3E823E3F3D0000973E00003F3ED13BE73D8E3E8D3D4F3DE53C0000993E0000B43D843ED63DFC3B6B3E000000000000AD3E923E0000133E0000073D0000CC3E00000000623E243DC93E9B3EA73D203E483E000000000000CD3EBA3C693D00000000553C0000213E0000083E0000B03E1A3EBE3E493ED23DAF3C823D983D00000000A03E000000000F3C0000C63C8E3B8A3E00000000993E0000B93E0000823E0000173E0000913B00000D3EAA3E000000008C3E913E3E3E00000000000000000000C93D0000153D0000B93D883E00000000000000006F3D00000000983E000000000000000000000000000000000000013F9B3E0B3E563E0000163E0000BF3DE43D00000000223E1B3E693DC53E000000000000E83D1D3E8B3E0000C63DDF3D000000000000000000000000163E4C3E423E0000AC3D7B3E453EF13D00000000683CA93CAC3D000000001B3EFA3CC33E00006E3E00000000000000000000033F000000000000683C723E000000006F3D0000613E023E00000000533E9E3EC03DB83D783E00000000983E0000000000000000C03DDC3D000000000000293EB33D0000D63D00000000AD3D0000000000000000803ED63D0000813ECA3EA33E0000223DD93EB33D00000000A33D0A3D533D00000000383E00000000783E00002C3E593E193E1E3EC13C0000000000000000DF3E9C3E0000D23E00005E3E453E00002D3E0000903E323E000000009F3E0000000000008A3DD13E873D0000E93CCE3C0000F53C573E0000923D00003B3DEC3EDF3D803E973CA73EAD3E0000AE3DA33DE83E0000A73E00000000000000001F3EA13D00000000333E823EB13E083E000000007B3ED33E553E103E0000533E00000000E73D0A3E1D3E0000FB3D00000000CA3D623E693C3F3D673E0000000000000000453E000000000000713C0000000000000B3E0000C63E953E00000000000000000000253B0000B33D0000BF3D0000143D0000903D923D4B3DBE3DA63E843D0000273E000000000000000000002F3E000000000000D13E00000000243E0000943E00000000993D00000000093EB83D203DA93D0000073E00000000B13E873D000000004B3D00000000133E0000A03D000000000000E23D0000843D0000663E363E543C973C00000000000000000000953E8B3D4D3E000000000000673E00005F3D00000000983D0000BC3D0000C03D0000763D533E9D3D000000000000F43DEE3D203E00000000000000000000E73E000000000000123EFD3E0000583E223D0000000000000000B73D00000000CB3CA93CDD3D0000CF3D00002F3E813D0000293D00000000263D193D0000203EDB3D00000000000000000000000000004E3E000000000000F13D00005E3D00000000DE3D123EBF3DD33D583D0E3E203C703E0000743E00003F3E000000000000000000008C3E643E9F3EB23D0000983D00000000000000004D3E0000293DD93D0000000000009B3DA33E0000A33B0000FE3C000000000000313C0000623E00005F3E0000A03DF23D00002F3CDA3DF43D0000123E00000000383B433E0000D33D703D0000633E00002E3E283E00000D3F0000000000000D3EDB3C00000000963D753EBD3D00005B3E0000FB3E0000343D6E3EDC3D00000F3D903D3D3E0000000000000000E53D00000000323E00000000A53D4C3E193E2D3D0000CD3D0000F43DB03C0000000000000000933E0D3EA73D000000000000FD3D00006B3D00000000000000000000413D000000000000373E000000000000AF3D00000F3E000000000000193E0000BF3B933D0000CC3E00002E3E0000AD3E00000000573EF43E000000000000A23E0000863E000000000000913E0E3FB43E0000613E153E0000A53E083EEE3D0000A43D443E0000493E00000000000000000000883C843D1E3D000000000000A23E000000000000963D0000B93D000000000000003B573E8F3D1D3E00000000C73B673D233DDE3D0000673E0000053E553E353D0000373ECB3D993D3E3EDC3E863E213D00000000963DB23D0000000000000000000000009B3E000000000000353ED63D0000683E000000000000133F133E2E3EA63E163E473E903E00000000983ED53C00000000000000000000743E8B3B083E093E00000000DC3DD23D00000000C23E663D383E5E3D00000000000000000000F13D0000403E933E093E000000005D3D253ED33D753BE83D0000000000000000000000000000903E5F3E000000006C3E543E000000000000793B163D0F3E1E3E00000000013F0000163E9B3EB23D0000000000008B3E0000823E00002A3EF83D5A3E000000000000113E603C0000000000000000C03EAE3D00007F3E563E00000000663DC63D5A3E00000000D53D000000000000A53D00008E3B0000C43C143E4A3E0000113EEA3D0000503DA13B0000883D423C00000000F83C523EE73C000000000000DF3DFA3B113E00000000CC3D00008F3D000000000000F63D00000000A03E00000000233E863E913E000000000000163E00000000443E553E00000000883B000000009A390000313D623EEA3D0000933DCF3D0000893E1F3E033E0000A33E0C3E963D00000000093D00000000B73D0000133E00000000B03D0000E63E053E0000413D2F3E843D000000000000BC3E493E1C3E0000000000008D3C953D00008A3E0000000000000000383D0000000000000000D73D000000001B3D00000000A63D913D583EEC3D000000000000000000000000883E00000000713D3A3E823D000000002B3E043DB23E023E000000000000003E1E3DCD3E00001C3E00000000000000003D3E833EB53D00009E3E0000403E0000000000004C3E00001A3E0000000000000000843D6F3D7F3E363DE43D193E0D3E783E0000F73D0000D93D0000983D000000004F3CF63D9D3DC33C000000003E3E00000000673EA53E513E00002E3F953DAC3EEE3A0000E13E000000000000A23E00000B3E0000C03E00000000AF3D0000AE3E513B00000000403E133EF03E273E0000AB3B0000423E00000000000000000000F13DBF3D00008F3ECB3E000000006B3EEC3CE63D0000BD3E0000153EFA3D0000000000000000A13DC13E00000000B63D0000923E5C3D7C3E0C3E343D0000883E00000000B23E00000F3D000000009A3D0000253D00000000000000000000613E0000000000000000303E0F3D183EE23D3E3D583E0000000000000000383D143B00004F3E183D0000000000000000373D0000000000001C3D1C3D000000000000083E00000A3D000000000000643E0000903E0000000000000000293E0000373E1D3E0000CB3C000000000000D23D4B3E823D0A3E0000423E0000FE3C000000000000503E00009A3E0000063D0000000000000000000000001A3E00000000B93C323E000000006E3D0000C83E00000000D53E643E00000000263E00009C3E00009A3D713E5E3E000000000000243E00000000B03E5A3C000000000000B03D00004F3EEC3B000000008E3E00000000D93CD53D0000813EE53B723D0000AA3EA23E0000143D0000773D323D000000000000D63D000000009E3D253E2F3E000000000000973D00000000963E00000000703E1C3E000000001A3D333D3F3E00000000363E000000000000573D0B3DE33D0000DB3E00000E3E0000763E0000C33DE83D703D133EBE3DE23D00000000783A000000000000F03ACE3D953DB03DB73DD33D00001A3E853E393E4F3E000000000000000000000000000000000000713E00000000CF3D0000DE3E1F3E000000000000B23D00000000233E000000000000843D00000000000000005A3D0000C93E00008E3E0000913D0000163E0000000000000000DC3E0000673E623E00000000000000009B3E9D3E553EF53D4A3E00000000993EC33E00000000523E0000F93B00000000000000000000A83E613D383E00009C3D0000000000007A3E00000000083E933D0000493E203E0000113D943E6E3E2F3E893E943EDD3D1F3E0000000000007D3E0000173D4D3D000000000000963E00006D3E163CA43E1E3E3E3D873E0000193EE63D000000000000A53E363DA03DD93D00000000E13D000000009E3E0000000000000000A73CBD3B00000000FF3D00001C3E0000753D0000E93D0000000000000000000000000000663C0000000000000000D93D000000000000043D393D0000F13A533E000000000000000000000000F53D0000E73D0000C13E9B3E0000233E7F3E00005D3E00006F3EE63D0000000000000000F93C0000000000000000063D583E00009C3E0F3E0000363D1A3C0000FC3DBF3ED83D00000C3D663DBE3E00000000B13E00003F3EF63C00004E3E00001B3C0000AA3E143E0000000000002A3E043FD03CE23D7D3AF53D00009A3D00002E3E0000333E9E3E00000000393D943E000000000000813D00000000BC3D283C4B3E000000000000A83DB53C0000ED3D0000933E0000343E00000000073E00008D3B143D5A3E0000CC3E0000A83E9F3D00000D3A553D293EAF3D673DC53E0000683D0000193E0000043C533D00000000C63D813E213D383E0000073D0000353D703E403E00000000A93D0000673DD83D603D00000000793D0000943B9F3E1C3A0000903EEB3D673D973D133E00008C3E0000CE3D863E2E3E000000000000053D000000000000000000000000D23D00000000000000000000F73D00002D3EE93DC83D0000000000003D3E0000AC3D4F3E00008D3E0000943E023B9E3E703EA83D4E3E000000005E3E6E3D0000BB3D883E4F3E0000913E6B3E0D3E0000C73E593E1C3E0000953E000000000000583E053EB23E5E3D00000000233DA83EC33D0000483E043DFE3D00000000000000000000523E2D3E0000B03D0000953E00000000073E00000000893EA93D00000000433DBB3D433E063B0000A03D863CCD3E363E283E0000000000000000243D0000000000000000C83D0000943C0000BD3E0000000000000000E93E0000713DB73E00000000DE3DC43D0000443E000000000000933D193E000000008B3C163E000000000000000000000000183D0000713D00004E3E00000000673DFA3D00000000F13D00005F3E063E953E0000E43D000000000000C93D0000403D00000000B33E0000AB3E873D6B3E000000000000C53DB63A00001B3E0000833E0000063D263D00000000000000000C3E0000E13E00003E3E0000F53E2C3E0000F33D773E00000000F93D000000003C3E0000503D00000000C73D0000C93C503E1A3E743E2F3E000000004E3D0000AC3D1B3E4E3EAD3E0000D63C333E00008D3E273E00000C3F593E00009F3D00000000853E0000DB3E583E183E9F3D8C3E0000BB3D00000D3EB43D193D633E000000008D3E4E3E000000000000000000000000693F0000000000001D3D1C3E0000000000005E3E00001C3EF93D493CA43C053D0000843EB03D1A3E8D3EFE3B000000006C3E00000000033E00002A3E000000000000A03C343E00007E3D0000313CBB3D9E3E7D3E0000363E000000000000743B00000000000000007B3D393E00000000733D773E0000503D0000773C000000000000073E0000573DA53D00004D3D593E0000423E533E0000943E4B3D00000E3E00000000D13D6B3EF63D3C3E053EBD3D323EE03C0000A53E793C00000000653E0000000000000000953E093E00000000C63DB83D913D5E3E2E3D0000A13D183EBF3C7D3D933E8B3DE93D00002A3E0000603D00005F3A243D893ECC3D0000D43D713E0000000000001F3D0000123E5F3DD33C00000000563E0000503ECC3C00000000113E0000000000003E3E00000000713E8B3C000000000000893E813DAF3DB93E053D00000000000000009C3E0000EB3D0000203EC23C0000B93D943D8D3E00009A3EA73D000000001D3E00003C3E000000000000000000000000CF3CDA3D0000000000000000000000000000943E00000D3E0000BE3D483ED33EF73D000000000000E53D00002C3E00004A3EAE3E0000B13DFF3DBE3E373EB13E7B3DAF3D0000000000000000803E00006F3D000000000000163E000000000000883E593E133E00000000073D083EA63D00000000843E00000000033E0000A83D000000008E3E00000000833D0000443E853E0000593E9F3C9F3E833E00000000233CCE3D000000000C3E00000000A33E00000000000000008A3DF83D4F3E0000000000000000FB3D1F3E0000000000000000323DE43D0000CD3E0000943E000000000000703D3D3E0000000000007E3E00000000473E573DCF3D673E223C153DEE3CB23E0000A23E00000000913E000000000000000000000000D13E283E0000063E513E933D00000000923D7F3E000000000000623E0000FF3C00000D3E0D3E0000293E183E933ECA3E00003C3D593E4F3D003E103D9B3DFD3E363D0000A33D00007A3B00003F3D0000453E0B3D000000000000543D0000D83D063D433D633BBA3D000000000000083E00000000F73E00000000233E000000000000E63D00000000733C243E000000000000933E813E000000000000D03E493EA93DB83E843E483C253C0000C83E203D333D6B3E713EFC3D00000000763BB03D000000000B3E000000009C3DA43E0000073D813E793E000000003D3ECC3E00006E3D00000000C73C00004A3E6C3E000000000000283BCF3EE63D0F3E0000000000000000B13D0000523E133E0000000000009B3DDA3D823E233D00000000000000000000B33E933E0000083E0E3E0000F43D000000000000313E000000000000673EFA3C00009F3E0A3F683D8D3E00000000E23D000000000000000000000000923E8E3D00009C3E243CAF3E0000913E00000000493EA33E683EF43D573EAC3E1F3E00000000AA3D013E1D3E0000103DB63EE33D2B3E0000000000000000F53D0000B43EA33E000000000000763A383E0000000000008A3E663D133E603E0000000000000000C93E133E0A3E00000000F83C0000F43E033C00000000493B433DE73DE73D00006C3B693E00000000B13D0000000000000000923D00003E3EAC3E343D2E3E393E0000000000007E3E0000C33D9D3E0000BC3C00007B3D0000853E0000BF3D2E3E1A3CE43D00000000663E00003B3E783E00000000863E9C3D983DC33D00001B3E283E0000983EFB3D313E2B3CA73E9F3E00000000083D0000393E9D3E000000000000193EA73D0000D93D843EEC3D923E0000613E0000CE3D013E0000E63E8F3E00000000413E0000C03D0000000000000000533D1A3EA43E393EB63E000000000000EE3D0000000000001D3DB93E00000000000000000A3F0000933E00000000000000008A3E4B3C0000000000000000093E9F3C0000000000000000B33A00000000D23D000000002A3E00009F3D00000000653D00001C3DA83A863D000000000000C43E443C8B3EC23E0000033C2E3E613E0000173EF13B000000000000823C00000000433E00000000B93E973D433D000000001B3E0000A93E0000913D893D0000A53D1D3C00000000F23C9F3ED93EB83D353E803E0000533E0000000000005D3D0000000000006B3D00000000413D0000000000000000303ECB3EC93E903DD83DAB3E813E0000FE3D0000683E9B3E000000001A3E0000203E0000283D783C00000A3E000000008D3D953CB43E0000000000005B3ED63D0000143E0000423E493EA03E533E413E523E000000000000FE3D000000001A3ED23EF53E00001C3F0C3F0000123E0000A13E00000000803E0D3E0000033E883DEC3C000000000000813E000000006C3D563D0000DC3CD53D2C3D0000000000000000443E0000573D000000004A3E0000503E833E00002E3C0000E23E00008F3D0000A43DE13D0000393EF23D000000000000"> : tensor<64x64xbf16> %cst_1 = arith.constant 0.000000e+00 : bf16 %0 = tensor.empty() : tensor<2x2x32x32xbf16> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> %1 = tensor.empty() : tensor<2x2x32x32xbf16> - %pack_2 = tensor.pack %cst_0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> + %pack_2 = linalg.pack %cst_0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> %2 = tensor.empty() : tensor<2x2x32x32xbf16> %3 = linalg.fill ins(%cst_1 : bf16) outs(%2 : tensor<2x2x32x32xbf16>) -> tensor<2x2x32x32xbf16> %4 = tensor.empty() : tensor<2x2x16x32x2xbf16> - %pack_3 = tensor.pack %pack_2 inner_dims_pos = [2] inner_tiles = [2] into %4 : tensor<2x2x32x32xbf16> -> tensor<2x2x16x32x2xbf16> + %pack_3 = linalg.pack %pack_2 inner_dims_pos = [2] inner_tiles = [2] into %4 : tensor<2x2x32x32xbf16> -> tensor<2x2x16x32x2xbf16> %expanded = tensor.expand_shape %pack [[0], [1], [2], [3, 4]] output_shape [2, 2, 32, 16, 2] : tensor<2x2x32x32xbf16> into tensor<2x2x32x16x2xbf16> %5 = linalg.generic {indexing_maps = [#map, #map1, #map2], @@ -29,11 +29,11 @@ func.func @chained_constant_packs(%arg0: tensor<64x64xbf16>) -> tensor<64x64xbf1 } -> tensor<2x2x32x32xbf16> %6 = tensor.empty() : tensor<64x64xbf16> %7 = tensor.empty() : tensor<2x2x32x32xbf16> - %pack_4 = tensor.pack %cst outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %7 : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> + %pack_4 = linalg.pack %cst outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %7 : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> %8 = tensor.empty() : tensor<2x2x32x32xbf16> %9 = linalg.fill ins(%cst_1 : bf16) outs(%8 : tensor<2x2x32x32xbf16>) -> tensor<2x2x32x32xbf16> %10 = tensor.empty() : tensor<2x2x16x32x2xbf16> - %pack_5 = tensor.pack %pack_4 inner_dims_pos = [2] inner_tiles = [2] into %10 : tensor<2x2x32x32xbf16> -> tensor<2x2x16x32x2xbf16> + %pack_5 = linalg.pack %pack_4 inner_dims_pos = [2] inner_tiles = [2] into %10 : tensor<2x2x32x32xbf16> -> tensor<2x2x16x32x2xbf16> %expanded_1 = tensor.expand_shape %5 [[0], [1], [2], [3, 4]] output_shape [2, 2, 32, 16, 2] : tensor<2x2x32x32xbf16> into tensor<2x2x32x16x2xbf16> %11 = linalg.generic {indexing_maps = [#map, #map1, #map2], @@ -45,7 +45,7 @@ func.func @chained_constant_packs(%arg0: tensor<64x64xbf16>) -> tensor<64x64xbf1 %13 = arith.addf %out, %12 : bf16 linalg.yield %13 : bf16 } -> tensor<2x2x32x32xbf16> - %unpack = tensor.unpack %11 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %6 : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> + %unpack = linalg.unpack %11 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %6 : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> return %unpack : tensor<64x64xbf16> } @@ -56,10 +56,10 @@ func.func @chained_constant_packs(%arg0: tensor<64x64xbf16>) -> tensor<64x64xbf1 // CHECK-SAME: %[[ARG0:.+]]: tensor<64x64xbf16> // CHECK-DAG: %[[CST_PACKED_1:.+]] = arith.constant dense<"0x0000AF3BA03D{{.*}}: tensor<2x2x16x32x2xbf16> // CHECK-DAG: %[[CST_PACKED:.+]] = arith.constant dense<"0x00000000CA3B{{.*}}: tensor<2x2x16x32x2xbf16> -// CHECK: tensor.pack %[[ARG0]] -// CHECK-NOT: tensor.pack +// CHECK: linalg.pack %[[ARG0]] +// CHECK-NOT: linalg.pack // CHECK: linalg.generic{{.*}}ins({{.*}}, %[[CST_PACKED]] : -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: linalg.generic{{.*}}ins({{.*}}, %[[CST_PACKED_1]] : -// CHECK: %[[UNPACK:.+]] = tensor.unpack +// CHECK: %[[UNPACK:.+]] = linalg.unpack // CHECK-NEXT: return %[[UNPACK]] : tensor<64x64xbf16> diff --git a/test/Passes/fold-pack-into-constant-weight.mlir b/test/Passes/fold-pack-into-constant-weight.mlir index ef1197b6a..516b63f0b 100644 --- a/test/Passes/fold-pack-into-constant-weight.mlir +++ b/test/Passes/fold-pack-into-constant-weight.mlir @@ -3,7 +3,7 @@ func.func @splat() -> tensor<8x2x1x1x32x32xi64> { %cst = arith.constant dense<1> : tensor<1x1x64x256xi64> %0 = tensor.empty() : tensor<8x2x1x1x32x32xi64> - %pack = tensor.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %0 : tensor<1x1x64x256xi64> -> tensor<8x2x1x1x32x32xi64> + %pack = linalg.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %0 : tensor<1x1x64x256xi64> -> tensor<8x2x1x1x32x32xi64> return %pack : tensor<8x2x1x1x32x32xi64> } @@ -23,13 +23,13 @@ func.func @non_splat() -> tensor<2x4x4x2xf32> { [49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0], [57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0]]> : tensor<8x8xf32> %0 = tensor.empty() : tensor<2x4x4x2xf32> - %pack = tensor.pack %cst inner_dims_pos = [0, 1] inner_tiles = [4, 2] into %0 : tensor<8x8xf32> -> tensor<2x4x4x2xf32> + %pack = linalg.pack %cst inner_dims_pos = [0, 1] inner_tiles = [4, 2] into %0 : tensor<8x8xf32> -> tensor<2x4x4x2xf32> return %pack : tensor<2x4x4x2xf32> } // TODO: Did not find a good way to escape multiples '[' // CHECK-LABEL: func.func @non_splat -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: [0.000000e+00, 1.000000e+00], [8.000000e+00, 9.000000e+00], [1.600000e+01, 1.700000e+01], [2.400000e+01, 2.500000e+01] // CHECK: [2.000000e+00, 3.000000e+00], [1.000000e+01, 1.100000e+01], [1.800000e+01, 1.900000e+01], [2.600000e+01, 2.700000e+01] // CHECK: [4.000000e+00, 5.000000e+00], [1.200000e+01, 1.300000e+01], [2.000000e+01, 2.100000e+01], [2.800000e+01, 2.900000e+01] @@ -51,13 +51,13 @@ func.func @non_splat_with_outer() -> tensor<4x2x4x2xf32> { [49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0], [57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0]]> : tensor<8x8xf32> %0 = tensor.empty() : tensor<4x2x4x2xf32> - %pack = tensor.pack %cst outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [4, 2] + %pack = linalg.pack %cst outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [4, 2] into %0 : tensor<8x8xf32> -> tensor<4x2x4x2xf32> return %pack : tensor<4x2x4x2xf32> } // CHECK-LABEL: func.func @non_splat_with_outer -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: [0.000000e+00, 1.000000e+00], [8.000000e+00, 9.000000e+00], [1.600000e+01, 1.700000e+01], [2.400000e+01, 2.500000e+01] // CHECK: [3.200000e+01, 3.300000e+01], [4.000000e+01, 4.100000e+01], [4.900000e+01, 5.000000e+01], [5.700000e+01, 5.800000e+01] // CHECK: [2.000000e+00, 3.000000e+00], [1.000000e+01, 1.100000e+01], [1.800000e+01, 1.900000e+01], [2.600000e+01, 2.700000e+01] @@ -79,13 +79,13 @@ func.func @non_splat_with_inner() -> tensor<2x4x2x4xf32> { [49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0], [57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0]]> : tensor<8x8xf32> %0 = tensor.empty() : tensor<2x4x2x4xf32> - %pack = tensor.pack %cst inner_dims_pos = [1, 0] inner_tiles = [2, 4] + %pack = linalg.pack %cst inner_dims_pos = [1, 0] inner_tiles = [2, 4] into %0 : tensor<8x8xf32> -> tensor<2x4x2x4xf32> return %pack : tensor<2x4x2x4xf32> } // CHECK-LABEL: func.func @non_splat_with_inner -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: [0.000000e+00, 8.000000e+00, 1.600000e+01, 2.400000e+01], [1.000000e+00, 9.000000e+00, 1.700000e+01, 2.500000e+01] // CHECK: [2.000000e+00, 1.000000e+01, 1.800000e+01, 2.600000e+01], [3.000000e+00, 1.100000e+01, 1.900000e+01, 2.700000e+01] // CHECK: [4.000000e+00, 1.200000e+01, 2.000000e+01, 2.800000e+01], [5.000000e+00, 1.300000e+01, 2.100000e+01, 2.900000e+01] @@ -108,13 +108,13 @@ func.func @non_splat_with_padding() -> tensor<2x4x2x5xf32> { [57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0]]> : tensor<8x8xf32> %0 = tensor.empty() : tensor<2x4x2x5xf32> %pad = arith.constant -1.0 : f32 - %pack = tensor.pack %cst padding_value(%pad : f32) inner_dims_pos = [1, 0] inner_tiles = [2, 5] + %pack = linalg.pack %cst padding_value(%pad : f32) inner_dims_pos = [1, 0] inner_tiles = [2, 5] into %0 : tensor<8x8xf32> -> tensor<2x4x2x5xf32> return %pack : tensor<2x4x2x5xf32> } // CHECK-LABEL: func.func @non_splat_with_padding -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: [0.000000e+00, 8.000000e+00, 1.600000e+01, 2.400000e+01, 3.200000e+01], [1.000000e+00, 9.000000e+00, 1.700000e+01, 2.500000e+01, 3.300000e+01] // CHECK: [2.000000e+00, 1.000000e+01, 1.800000e+01, 2.600000e+01, 3.400000e+01], [3.000000e+00, 1.100000e+01, 1.900000e+01, 2.700000e+01, 3.500000e+01] // CHECK: [4.000000e+00, 1.200000e+01, 2.000000e+01, 2.800000e+01, 3.600000e+01], [5.000000e+00, 1.300000e+01, 2.100000e+01, 2.900000e+01, 3.700000e+01] @@ -136,13 +136,13 @@ func.func @non_splat_with_inner_2() -> tensor<2x4x4x2xf32> { [49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0], [57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0]]> : tensor<8x8xf32> %0 = tensor.empty() : tensor<2x4x4x2xf32> - %pack = tensor.pack %cst inner_dims_pos = [0, 1] inner_tiles = [4, 2] + %pack = linalg.pack %cst inner_dims_pos = [0, 1] inner_tiles = [4, 2] into %0 : tensor<8x8xf32> -> tensor<2x4x4x2xf32> return %pack : tensor<2x4x4x2xf32> } // CHECK-LABEL: func.func @non_splat_with_inner_2 -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: [0.000000e+00, 1.000000e+00], [8.000000e+00, 9.000000e+00], [1.600000e+01, 1.700000e+01], [2.400000e+01, 2.500000e+01] // CHECK: [2.000000e+00, 3.000000e+00], [1.000000e+01, 1.100000e+01], [1.800000e+01, 1.900000e+01], [2.600000e+01, 2.700000e+01] // CHECK: [4.000000e+00, 5.000000e+00], [1.200000e+01, 1.300000e+01], [2.000000e+01, 2.100000e+01], [2.800000e+01, 2.900000e+01] @@ -164,13 +164,13 @@ func.func @non_splat_with_inner_3() -> tensor<4x2x2x4xf32> { [49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0], [57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0]]> : tensor<8x8xf32> %0 = tensor.empty() : tensor<4x2x2x4xf32> - %pack = tensor.pack %cst inner_dims_pos = [0, 1] inner_tiles = [2, 4] + %pack = linalg.pack %cst inner_dims_pos = [0, 1] inner_tiles = [2, 4] into %0 : tensor<8x8xf32> -> tensor<4x2x2x4xf32> return %pack : tensor<4x2x2x4xf32> } // CHECK-LABEL: func.func @non_splat_with_inner_3 -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: [0.000000e+00, 1.000000e+00, 2.000000e+00, 3.000000e+00], [8.000000e+00, 9.000000e+00, 1.000000e+01, 1.100000e+01] // CHECK: [4.000000e+00, 5.000000e+00, 6.000000e+00, 7.000000e+00], [1.200000e+01, 1.300000e+01, 1.400000e+01, 1.500000e+01] // CHECK: [1.600000e+01, 1.700000e+01, 1.800000e+01, 1.900000e+01], [2.400000e+01, 2.500000e+01, 2.600000e+01, 2.700000e+01] @@ -186,13 +186,13 @@ func.func @non_splat_with_inner_and_outer() -> tensor<1x2x2x2x2xf32> { %cst = arith.constant dense <[[[[0.0, 1.0, 2.0, 3.0], [4.0, 5.0, 6.0, 7.0]], [[8.0, 9.0, 10.0, 11.0], [12.0, 13.0, 14.0, 15.0]]]]> : tensor<1x2x2x4xf32> %0 = tensor.empty() : tensor<1x2x2x2x2xf32> - %1 = tensor.pack %cst outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] + %1 = linalg.pack %cst outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [2] into %0 : tensor<1x2x2x4xf32> -> tensor<1x2x2x2x2xf32> return %1 : tensor<1x2x2x2x2xf32> } // CHECK-LABEL: non_splat_with_inner_and_outer -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: [0.000000e+00, 1.000000e+00], [4.000000e+00, 5.000000e+00] // CHECK: [8.000000e+00, 9.000000e+00], [1.200000e+01, 1.300000e+01] // CHECK: [2.000000e+00, 3.000000e+00], [6.000000e+00, 7.000000e+00] diff --git a/test/Passes/lower-packs-and-unpacks-without-transpose.mlir b/test/Passes/lower-packs-and-unpacks-without-transpose.mlir index b9d741731..1d9d48438 100644 --- a/test/Passes/lower-packs-and-unpacks-without-transpose.mlir +++ b/test/Passes/lower-packs-and-unpacks-without-transpose.mlir @@ -4,7 +4,7 @@ #map2 = affine_map<(d0, d1, d2, d3) -> (d0 * 32 + d2, d1 * 32 + d3)> func.func @single_packed_arg(%arg0: tensor<128x512xf32>, %arg1: tensor<128x512xf32>) -> tensor<128x512xf32> { %0 = tensor.empty() : tensor<4x16x32x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %1 = linalg.generic {indexing_maps = [#map, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%pack : tensor<4x16x32x32xf32>) outs(%arg1 : tensor<128x512xf32>) { ^bb0(%in: f32, %out: f32): linalg.yield %in : f32 @@ -28,18 +28,18 @@ func.func @single_packed_arg(%arg0: tensor<128x512xf32>, %arg1: tensor<128x512xf #map2 = affine_map<(d0, d1, d2, d3, d4, d5) -> (d0, d1, d3, d4)> func.func @revert_all_packing(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf32>, %arg2: tensor<128x256xf32>) -> tensor<128x256xf32> { %0 = tensor.empty() : tensor<4x16x32x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %1 = tensor.empty() : tensor<8x16x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> %2 = tensor.empty() : tensor<4x8x32x32xf32> - %pack_1 = tensor.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> + %pack_1 = linalg.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %pack_0 : tensor<4x16x32x32xf32>, tensor<8x16x32x32xf32>) outs(%pack_1 : tensor<4x8x32x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %4 = arith.mulf %in, %in_2 : f32 %5 = arith.addf %out, %4 : f32 linalg.yield %5 : f32 } -> tensor<4x8x32x32xf32> - %unpack = tensor.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> + %unpack = linalg.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> return %unpack : tensor<128x256xf32> } @@ -63,11 +63,11 @@ func.func @revert_all_packing(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256x func.func @only_keep_constant_packed_non_prepacked(%arg0: tensor<128x512xf32>, %arg1: tensor<128x256xf32>) -> tensor<128x256xf32> { %cst = arith.constant dense<1.000000e-03> : tensor<512x256xf32> %cst_empty = tensor.empty() : tensor<8x16x32x32xf32> - %cst_packed = tensor.pack %cst outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %cst_empty : tensor<512x256xf32> -> tensor<8x16x32x32xf32> + %cst_packed = linalg.pack %cst outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %cst_empty : tensor<512x256xf32> -> tensor<8x16x32x32xf32> %0 = tensor.empty() : tensor<4x16x32x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %1 = tensor.empty() : tensor<4x8x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> %2 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %cst_packed : tensor<4x16x32x32xf32>, tensor<8x16x32x32xf32>) outs(%pack_0 : tensor<4x8x32x32xf32>) { ^bb0(%in: f32, %in_1: f32, %out: f32): %3 = arith.mulf %in, %in_1 : f32 @@ -75,7 +75,7 @@ func.func @only_keep_constant_packed_non_prepacked(%arg0: tensor<128x512xf32>, % linalg.yield %4 : f32 } -> tensor<4x8x32x32xf32> // NB: unpack's outer_dims_perm should match those of corresponding pack - in case of elision it should match with an identity perm - %unpack = tensor.unpack %2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> + %unpack = linalg.unpack %2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> return %unpack : tensor<128x256xf32> } // CHECK: #map = affine_map<(d0, d1, d2, d3, d4, d5) -> (d0, d3, d2, d5)> @@ -109,18 +109,18 @@ module { %dim = tensor.dim %arg0, %c0 : tensor %0 = affine.apply #map()[%dim] %1 = tensor.empty(%0) : tensor - %pack = tensor.pack %arg0 padding_value(%cst_0 : f32) outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor -> tensor + %pack = linalg.pack %arg0 padding_value(%cst_0 : f32) outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor -> tensor %dim_1 = tensor.dim %arg1, %c0 : tensor %2 = affine.apply #map()[%dim_1] %3 = tensor.empty(%2) : tensor - %pack_2 = tensor.pack %arg1 padding_value(%cst_0 : f32) inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %3 : tensor -> tensor + %pack_2 = linalg.pack %arg1 padding_value(%cst_0 : f32) inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %3 : tensor -> tensor %4 = linalg.generic {indexing_maps = [#map1, #map2, #map3], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %cst : tensor, tensor<8x16x32x32xf32>) outs(%pack_2 : tensor) { ^bb0(%in: f32, %in_3: f32, %out: f32): %5 = arith.mulf %in, %in_3 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor - %unpack = tensor.unpack %4 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor -> tensor + %unpack = linalg.unpack %4 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor -> tensor return %unpack : tensor } } @@ -137,14 +137,14 @@ module { // CHECK: %[[M_ROUNDED_UP:.*]] = affine.apply {{.*}}()[%[[M_DUP]], %[[M]]] // CHECK: %[[ARG0_PADDED:.*]] = tensor.pad %[[ARG0]] low[0, 0] high[%[[M_ROUNDED_UP]], 0] // CHECK: %[[M_PADDED:.*]] = tensor.dim %[[ARG0_PADDED]], %[[C0]] - // CHECK: %[[NUM_CHUNKS_PADDED_M:.*]] = arith.divui %[[M_PADDED]], %[[C32]] + // CHECK: %[[NUM_CHUNKS_PADDED_M:.*]] = arith.divsi %[[M_PADDED]], %[[C32]] // CHECK: %[[EXP0:.+]] = tensor.expand_shape %[[ARG0_PADDED]] {{\[}}[0, 1], [2, 3]{{\]}} output_shape [%[[NUM_CHUNKS_PADDED_M]], 32, 16, 32] : tensor into tensor // CHECK: %[[M_ARG1:.*]] = tensor.dim %[[ARG1]], %[[C0]] // CHECK: %[[M_ARG1_DUP:.*]] = tensor.dim %[[ARG1]], %[[C0]] // CHECK: %[[M_ARG1_ROUNDED_UP:.*]] = affine.apply {{.*}}()[%[[M_ARG1_DUP]], %[[M_ARG1]]] // CHECK: %[[ARG1_PADDED:.*]] = tensor.pad %[[ARG1]] low[0, 0] high[%[[M_ARG1_ROUNDED_UP]], 0] // CHECK: %[[M_ARG1_PADDED:.*]] = tensor.dim %[[ARG1_PADDED]], %[[C0]] - // CHECK: %[[NUM_CHUNKS_PADDED_M_ARG1:.*]] = arith.divui %[[M_ARG1_PADDED]], %[[C32]] + // CHECK: %[[NUM_CHUNKS_PADDED_M_ARG1:.*]] = arith.divsi %[[M_ARG1_PADDED]], %[[C32]] // CHECK: %[[EXP1:.+]] = tensor.expand_shape %[[ARG1_PADDED]] {{\[}}[0, 1], [2, 3]{{\]}} output_shape [%[[NUM_CHUNKS_PADDED_M_ARG1]], 32, 8, 32] : tensor into tensor // CHECK: %[[RES:.+]] = linalg.generic {{.*}} ins(%[[EXP0]], %[[CST]] : tensor, tensor<8x16x32x32xf32>) outs(%[[EXP1]] : tensor) // CHECK: %[[COL:.+]] = tensor.collapse_shape %[[RES]] {{\[}}[0, 1], [2, 3]{{\]}} : tensor into tensor diff --git a/test/Passes/pack-folding.mlir b/test/Passes/pack-folding.mlir index a27f622aa..30b023e7a 100644 --- a/test/Passes/pack-folding.mlir +++ b/test/Passes/pack-folding.mlir @@ -7,26 +7,26 @@ // CHECK-LABEL: func.func @pack_fn_small func.func @pack_fn_small() -> tensor<5x5x1x2x2xf32> { - // CHECK-NOT: tensor.pack + // CHECK-NOT: linalg.pack // CHECK-NEXT: %[[CST:.*]] = arith.constant dense<0.000000e+00> : tensor<5x5x1x2x2xf32> // CHECK-NEXT: return %[[CST]] : tensor<5x5x1x2x2xf32> %exp = arith.constant dense<[[[[[0.000000e+00, 1.513670e-01], [1.298830e-01, 1.062010e-02]]], [[[3.757480e-04, 9.814450e-02], [2.988280e-01, 1.123050e-02]]], [[[0.000000e+00, 0.000000e+00], [0.000000e+00, 5.004880e-02]]], [[[1.289060e-01, 0.000000e+00], [1.483150e-02, 0.000000e+00]]], [[[0.000000e+00, 0.000000e+00], [0.000000e+00, 0.000000e+00]]]], [[[[0.000000e+00, 0.000000e+00], [0.000000e+00, 0.000000e+00]]], [[[1.562500e-01, 0.000000e+00], [0.000000e+00, 1.318360e-01]]], [[[2.070310e-01, 6.494140e-02], [0.000000e+00, 1.542970e-01]]], [[[1.865230e-01, 3.886720e-01], [1.118160e-01, 0.0942381992]]], [[[1.884770e-01, 1.445310e-01], [0.000000e+00, 0.000000e+00]]]], [[[[0.000000e+00, 0.000000e+00], [0.000000e+00, 2.285160e-01]]], [[[0.000000e+00, 0.000000e+00], [2.490230e-01, 0.000000e+00]]], [[[0.000000e+00, 2.539060e-01], [4.913330e-03, 0.000000e+00]]], [[[0.000000e+00, 0.000000e+00], [0.0991209968, 2.563480e-02]]], [[[0.000000e+00, 0.000000e+00], [1.044920e-01, 0.000000e+00]]]], [[[[0.000000e+00, 2.421880e-01], [0.0991209968, 1.718750e-01]]], [[[0.000000e+00, 2.465820e-02], [2.490230e-01, 6.201170e-02]]], [[[0.000000e+00, 0.000000e+00], [2.773440e-01, 6.054690e-02]]], [[[0.000000e+00, 2.285160e-01], [0.0737304985, 2.353520e-01]]], [[[0.000000e+00, 0.000000e+00], [0.000000e+00, 2.392580e-02]]]], [[[[1.239010e-02, 2.233890e-02], [3.984380e-01, 0.000000e+00]]], [[[0.0961913987, 0.000000e+00], [0.000000e+00, 1.201170e-01]]], [[[0.000000e+00, 0.000000e+00], [3.613280e-02, 2.226560e-01]]], [[[0.000000e+00, 6.079100e-02], [2.349850e-03, 0.000000e+00]]], [[[4.394530e-02, 0.000000e+00], [2.216800e-01, 0.0932616963]]]]]> : tensor<5x5x1x2x2xf32> %cst = arith.constant dense<[[[[0.000000e+00, 1.298830e-01], [1.513670e-01, 1.062010e-02]], [[3.757480e-04, 2.988280e-01], [9.814450e-02, 1.123050e-02]], [[0.000000e+00, 0.000000e+00], [0.000000e+00, 5.004880e-02]], [[1.289060e-01, 1.483150e-02], [0.000000e+00, 0.000000e+00]], [[0.000000e+00, 0.000000e+00], [0.000000e+00, 0.000000e+00]]], [[[0.000000e+00, 0.000000e+00], [0.000000e+00, 0.000000e+00]], [[1.562500e-01, 0.000000e+00], [0.000000e+00, 1.318360e-01]], [[2.070310e-01, 0.000000e+00], [6.494140e-02, 1.542970e-01]], [[1.865230e-01, 1.118160e-01], [3.886720e-01, 9.423820e-02]], [[1.884770e-01, 0.000000e+00], [1.445310e-01, 0.000000e+00]]], [[[0.000000e+00, 0.000000e+00], [0.000000e+00, 2.285160e-01]], [[0.000000e+00, 2.490230e-01], [0.000000e+00, 0.000000e+00]], [[0.000000e+00, 4.913330e-03], [2.539060e-01, 0.000000e+00]], [[0.000000e+00, 9.912100e-02], [0.000000e+00, 2.563480e-02]], [[0.000000e+00, 1.044920e-01], [0.000000e+00, 0.000000e+00]]], [[[0.000000e+00, 9.912100e-02], [2.421880e-01, 1.718750e-01]], [[0.000000e+00, 2.490230e-01], [2.465820e-02, 6.201170e-02]], [[0.000000e+00, 2.773440e-01], [0.000000e+00, 6.054690e-02]], [[0.000000e+00, 7.373050e-02], [2.285160e-01, 2.353520e-01]], [[0.000000e+00, 0.000000e+00], [0.000000e+00, 2.392580e-02]]], [[[1.239010e-02, 3.984380e-01], [2.233890e-02, 0.000000e+00]], [[9.619140e-02, 0.000000e+00], [0.000000e+00, 1.201170e-01]], [[0.000000e+00, 3.613280e-02], [0.000000e+00, 2.226560e-01]], [[0.000000e+00, 2.349850e-03], [6.079100e-02, 0.000000e+00]], [[4.394530e-02, 2.216800e-01], [0.000000e+00, 9.326170e-02]]]]> : tensor<5x5x2x2xf32> %0 = tensor.empty() : tensor<5x5x1x2x2xf32> - %res = tensor.pack %cst inner_dims_pos = [2] inner_tiles = [2] into %0 : tensor<5x5x2x2xf32> -> tensor<5x5x1x2x2xf32> + %res = linalg.pack %cst inner_dims_pos = [2] inner_tiles = [2] into %0 : tensor<5x5x2x2xf32> -> tensor<5x5x1x2x2xf32> %zero = arith.subf %exp, %res : tensor<5x5x1x2x2xf32> return %zero : tensor<5x5x1x2x2xf32> } // CHECK-LABEL: func.func @pack_fn func.func @pack_fn() -> tensor<8x2x1x1x32x32xf32> { - // CHECK-NOT: tensor.pack + // CHECK-NOT: linalg.pack // CHECK-NEXT: %[[CST:.*]] = arith.constant dense<0.000000e+00> : tensor<8x2x1x1x32x32xf32> // CHECK-NEXT: return %[[CST]] : tensor<8x2x1x1x32x32xf32> %exp = arith.constant dense<"0x87DC18BD89795CBDFF5FABBD1791E13CFDA2F63B8A1EF73CF2BA803C5BCD373B69B7463CD5458B3C14DC4D3DF1FF87BD26A0F5BDAC5039BB5E62BE3D25DE953B84A3883D272234BA0A96FEBB3148773C838EA23CDDD8C53DF3D2DA3C1FA03DBD3C370A3D477E403CC09191BC436910BCA7CEDF3C33011BBE03E82F3CD5D66C3CC326F83BE8290E3C7387C83CEBFB15BDC93EF1BB7F575F3DF55B3BBD0F2A1C3B5C24FAB90F513A390E1727BC010452BA7373BC3C5FD3A7BE6BEA7AB9B4DE3EBD6FAB5A3D33DC64BD7EFB463EFE70103D6C51A4BCD8DDB8BB97FF953B1BBD293D62FB0EBC64292CBDEA8C8CBEACE91A3DE922D2BCED05A43C1A41F0BC414F16BD408F9FBCE0F016BCD40881BC6450B73CAB9C23BC42CFEEBC1D587B3E76FA403C4A0E043B71B9293CEADCA13B90E4EEBC430787BCED316B3D441C333C8DB3103D5009283C47E0ADBD02DE63BE2018CABCF32E243DD3C7003C400AC43C0DA8AE3AABC5DBBB9E8DD4BAB489CABD7A7F9BBC0EB16ABCBE7504BC6766A13D80F5A3BCEF2D553C80D66CBC09A5D2BBEC0B853CB76CBA3C8165473DCCA6163DF90D113C9CE95B3CAE489B3B3607C23C342DFEB93DEFF9BCB0D689BC52D200BC804294BB26D0BFBC27283BBDF127C0BBA7A9583DBD6FBD3B791AB43BB9B7913C2A7023BC431481BB125DB2BB723A233DA9C55E3C4FD8CFBA09FDF8BA00A9FBBC0DD8F13B46992F3CAF25D53D6D1E1C3DFCA3A63D87A8923C5E2C593D78BA7EBC3FBAE53CA61B4D3C16B199BD9E2F9E3B849A69BDC93D3D3B8A7B563A5B6B3E3D56CA433CAC13523B9CE6113B38D021BC9AD608BB097EEC3C9DD89B3CED3D35BD3DB94D3CD5EEC43A1BF99E3D776C1DBC7081083E67965A3DF28BCC3CAAA2AA3C0F027438DF15DDBBA5A1AC3C4A3A2D3D89DF0B3C040C873C1F06293D12DFBEBB8BB1EBBD3781883CB19E31BA88D7663D6C5BF4BC8565593C26831FBAAB7A9C3CC39BA2BEC235063D5066583CA7D7A33A7D1D21BC12027FBD43D59A3C639E81BCA403603DF82BC63B4D0E2A3B0D3A8ABCDA9407BD952A94BC48F2AC3CE8AA8B3CA2FC863A729AD2BC41DFA7BBA3DA553BDDBC6A3C13205C3C58006DBDEF4653BDA450BA381BDE6CBC98BEF23ABF1FAA3BFD18873C363C07BC8D5F743B1EE9E3BBB77605BC082D873C511F17BEC9A32ABE1F1AD4BD4294BB38045B723B71CCB43C95D9A7BC6A5BAEBC530DCABB20E3BF3DD5A3A93BC4A485BCB797D3BBA23CE23B1804813C6B013C3C4D53803D0221A33C906331BD2B88193CA1F2183DCBFC813A1ACB60BDBC2F693C578652BDBE1121BD728656BDC419AEBD1E1114BCA79212BE647024BDD126203C6D604E3ADD68A13B5385113C135E0EBEEF883ABD3E1CA0BD9CBBAD3B657B8DBD0539033D8BB7D1BCC2F42B3E3A7D933CFD76B53CEF6B32B949C749BC2417F53B0B7AE6BA661B57BBD080143DE980EBBC12F87E3D12B25C3C92DB0EBC00F5003DA51055BC0E55763CACFF863BCFF289BC2C4BA13DF0AE4E3BF0F6D2BB74A2633DC7E11DBBB4B7AC3C77084BBC387136BB445E00BB2CB30B3B8F7B94BC952C7CBCEF6B3438FF69FD3CED0ED43B783621BC09B9E2BACED595BC24BB103D2A5D333B64F75FBD13DCEDBD757CDA3D6E419FBCC1E30A3C085B2D3A1663903AA56710BD5E15213DDFD49E3D4FA0D8BCC710153D2B6A443BC671F63DD066A2BDAD8898BD4C2F5A3CF706873CFC5A223CFAF1973DD4A549BCE6EAAE3D2448673C32CEAF3D53ADE5BB229DA83CABB40ABDF397453D5D10CDBD4520C83CB64DDFBBBF7D19BC9CE2983C51FF663BBE08B7BBEC4D853C603E393DDF3C6FBE98FD573B87D23ABCA3CD373C0DF2153A51E92B3B371D20BD3088803E6CFA26BBCDB300BDCB52123D5C92513EFB6BD9BCB2D41FBF8AC6D73A1D19343BC33B9E3BDBA3473C6C8153383683A9BCDCDECA3E45CE0C3D94F48BBCDD153FBC5ABE4E3DDDB84C3DB55DD0BCB32988BEF5AC12BDB0D8B4BDB3910ABDA4D74A3D539D03BDAC74AF3C8F5B3CBC09F234BD75850B3B5FBB9FBCE7A9C5BD09FEFCBB063A97BC9FD57C3D938C44BC0C4BEB3C410DFE3CEBB4AABAEE2A12BD57169DBC539B3B3B5B7E07BDD57051BDF49577BC51952E3DBC1D173D8C913E3DC2C2FCBD157C9F3C25811C3CD19A3ABC6B755DBC8907593D84FAF33A71683C3C38E27D3DFAFB5DBABA6E1C3D503B203CFD87E3BB49D853BD879A0CBC5D7D253DC584FBBBA0EDF2391D71003D4AFFE23D372A373C4FF79DB95DF58D395789B23A5522003DBDEE17BDD80F50BB97B92A3DFB0E013D701BCDBB797B25BC2D2FA9B9F0A57FBC2A702EBD18BD2C3BF82119BD195ED7BD164A00BE8474233DF81FFFBC77A30F3DCA48533B440324BAE954183B790AB4BD211E57BCB8C7D03B2FC048BCB029283BF554C4BCC7E634BD7646953C84C7F9BB8D2E80BC8DF8683A82C420BCAFE7EAB9208F30BD979D73BDF7E07CBCFFF7D0BB31C98EBC947E79BD2338053D5A54AB3C710E08BDCBEF4CBC2AD654BD0B45FA3D852A13BE38B9DB3CB81B98BD5C68803C979CB6BC982674BA17C5343ACD232CBD957766BD8C4DBD3BA6861FBD158C413BB0C10D3D1CFF8BBC54EF45BCFE669F3B7E2D7F3CD58D0ABC021A213D216A11BAACBD923B0A2982BDE3D881BCBA60E1BCBCF7213CB201853D75DDA73CF88B4EBDDE4552BD8027843CC05773BD149D4CBC07EA61BB90D7053D6651973B0BBFC23D3254FC3C56312CBDED0F003DAE62D0BC1F6F113C55AD8C3C51D071BD97CE2A3DA197A7BCEB2D593CEAA8D53DD71AFDBC4132E63CF32A063DFB5D9DBC3C9902BDC23B31BC6CE742BCE82C11BD2105F8BA32E72B3D84332A3DAB0E35BD158A88BC393A78BCE460193D3F2F6EB9F8D06538A5A7E3BB9773C13C12E679BC5610823D51F45CB98EC1FCBB8D8F453D8FA451BCF156503C8281BFBC6A6104BD954387BDC67F843CD5799C3CC1FEBE3DDE872EBCCF06893DB5D2123BDEA2D53C4DBEDABBCC8C1B3B75B8C7BC138BABBC3A41B83CC8583D3CCB2518BCE2F73ABCC50EDEBB595B20BC41F4833D89DB24BC4DC3563D4158003DA3321FBE11E238BD59DAA93C720E35BC7348E03BB9305CBCDCE495BAF7B34FBDBF809ABCF49F6ABEA39F06BCB03C423C8513403D4693BFBD67A6B23B6DABA8BCE6EA4CBBB5D7F43B5DF0F6BCA13CAC3B045B96BD292D1DBD330C153D8B729ABCE79306BEA071F23A2F8115BEAF30F5BCE0F255BCA46D2A3AC352BB3C83C1A33C2C07AABCB20D7A3C57B18B3D0A724CBC692383B893D3BF3B43E061BB3B4E3E3AEE22D03C2A35363D1A239F3E78B29D3CD26172BD429ECBBDD83C0FBDBA9BE2BDC9FE0CBEFFAE05BDEEEDEE3C0DC20FBC69FCC43C4E73E3BB4D814ABB923EC2BDE36B583D02B696BC67D13E3CBEF5193DCC42D5BB6F71F83A87B894BC39B183BB8F36D93CE587C93BE689AF3BAD1819BC2D7E4ABC563F2AB90E580DBAA3EE503CF627C03BE54BC8BB082849BD8E36B93A244696BCBEAA163D63BE54BDB7498CBD6FE3C73C4B0C853CDFE768397267DA3B6B8F85BC665941BAEFC7FB394CF414BD93B9CABB12CCF4BB55AF82BC90C3E0BC12B22ABC4A64A3BB897A23BEDDE90DBE92F326BE68A190BC6435603DED1898BB096CB73C69CC8C3CC685753D806DD3BDF002003C4423DFBD38898E3CA89B1DBE52D038BDF106113D1398D8BB415B58BA99E2E1BB31F214BE6A9017BDB76299BD14AD0BBD07940FBD32B433BE90099BBC8E4CCDBD00A1C8BD8972F0BD295257BCAC5CAFBA4A6FC83C4A8D70BDAA1B50BDCCF8D1BC9C3480BC6C15CB3C2618DD3CF211D9BCF59F5D3A1BA312BD65592DBC12391D3D00F8233D3F38263D839D38BC466405BDAF02BFBCA59998BCDB566DBCD2F50FBC44FF3EBDC86EE0BC915F7DBC7893AB3B2543AABCBB23C1BD4E6674BD27DEBE3C9B627CBD99FF5CBCBCD1133D9A48E8BC2EC365BD9C13313D4AF56DBB14D3BE3CC1B8903D6346133CB678AE3C9746A0BBEB82463C182E98BB38F4CFBCD30764BC77A3A73CB17E493C9AD10FBD2BE61F3D87CAA43D544E62BA979637BC51C2D13BAE959E3CB2D31E3AF15898BCA5CD4ABD545260BC7D0C453DF9A05F3C08D50EBEB1BB0EBC2722C0BB07F01F3CBA42AABC6EEF88BCF986613D4F7F8CBC6F7983BEC4170E3D6F3E043D8634FF3A9972B73C824183BB66C11E3B935936BD55E1383C82F223BE310CC6BB1B655E3D76565F3DB4D698BD3DF29BBB210930BCEDF243BCCCB25CBB8B8D233E3672043C2E9000BC77B3B4BD39B14FBCB478FA3B27F2D1BD0476263C037902BE09D530BCD3046FBB0B47323DBB1BCA3C7895DEBD9D068BBCBF6A99BC56D83A3D272DC73B2B751E3D8042DDBADE76933D6DD72EBD6A3BD3BCABC4DABD1D0689BB5AB48FBD90DF0EBBDF9C49BCEC6D1D3AC91AD33BF1F75FBC8FC50BBD106812BDBBE22C3C78E622BE2CEA463C0EE2AABD2BA3483B534C2DBE4E2129BD20C939BE580868BC1A991FBCBB82883C577C09BD112BC6BBA336B8BB664A24BD9C7B403C30F12F3DEC6151BC5881E53B0E63A7BC70D5AF3C299550BCBD3FBABC7EC100BCC03BE3BC2756513CF2E184BC0A4BBDBBD7E033BDF81FDB3AE94D533B6D0E913CAE7B07BCED9DDDBB9AE28D3C8D5A15BD118F803CE2E482BCD2F891BC8EE4F2BC4E66473D3BF7E9BB441E12BC9FF700BE771A94BD9A582BBDF65D0A3D32A5363DD0091BBCF43499BC0F6375BBA28ECABD67D2CBBC5B12ACBCE3A6513C53324ABC85C1A0BBB1A75ABD169F04BC8FD4333BBEBF053B348A63BCB51FC4BD227DACBB2DB88ABD2EBB95BB2C6586BDE5ACCB3B9C9B1EBCF818203E8CE57C3C5F2725BB3DC50F3D8089493CDEA9D43CDE0DD2BD5CFF3DBECCD61DBC7DCC03BCD59A8E3DC7F3043BB7D371BD23C60A3C13FEBC3CF9FBAD3C7BE2553BCDFED1BC4DBDAF390FC728BDD6C422BDAEC69F3CC6DBBEBC5925B73B490823BC5F34B6BB8AD25D3DAE3B553B850956BDD40E6FB71E9107BED77FC7BCA30F27BDDE4390BDEA3315BE95E6DCBAFD48B73B624F5F3C91B66D3E3A63853D78A01C3D62858C3C4A01633DE00707BD83CB2E3CD91C2F3C12540FBE0B9C2EBC21824ABD71D6323D8F2BC1BBCB6426BC3C5EABBDF1C010BC3614D83B458B773DEFEEC23C76D00BBDF9B86ABB6AD1BCBD9DBA953CB30CB8BCA9F8213DCF4F8B3D472612BCE4A9BEBB8945833D829C55BCD683F53CF5AA393CB31E9D3C688F923CE3EE1DBC4F4F9CBCC398863C881F033E92D7563CA74C133B32AB14BA22FBF03B6B2DEE3C64278F3C2D58E7BE0CA92A3B039712BDD305E6BC19A03FBED2AE9DBE04ADAEBD5870D6BC74A6DD3BBCAE2FBC4B45A53CE4163FBCE209B4BC882198BED0561D3DA7BDB4BC9CA8BA3C5C01933909911EBDF741C2BDCCC5E2BD292D283BAC73F1BC914AA43C77C5443C9A50DB3C33A4ADBBD8308DBBFCC427BDDA298ABC38EC5FBCEA7ABABD90BC1A3B5FAF623CE7F7B4BD525883BD0FB8CC3B9AC439BB9FC654BB5A9B193DD8E883BC68C292BC075205BD8E13E1BCC08AA93C8723F8BB216CE1BCF2B389BB834EB5BCC684203BD53638BD2176593B23677ABBFD6587BBEDF1EA3B952E1F3A12D6333C569F873EEEE6E03ACB821C3C5E4D623B617E18BC7164CFBC2254EEBC037E95BDA952BFBCEA9F933B0E7E6C3DD01656BE301AB1BEF748983D5ED6DF3CD7CBD73A7C7E2A3C27B44FBC8AA018BC6BE0B0BC00F573BEF061633C4E2516BC1DC06CBA45533B3D3161C3BC05FF3238C26457BE94027CBDC7744ABD3639423B40A2643D42477F3C89599FBCA124383D530E48BD75E60BBD16B06EBB11C9323C3AAC12BC918C403DD166F0BA0919103CB51B5B3B9F5D37BBC3F9F73AD34CFEBC8CE40ABBF23CE4BC8D12983B91B61E3CDA6487BD5D51A7BBFEB4C03D13AD02BDFA91F1BC06EC0ABD403C46BCB9B1A1BDCA21DABD0B38403AD6A1F8BC27F790BBA5D68B3C77ED81BB3F8F08BC1A521BBCB978DBBCDDBAEBBC7B054CBBD293A8BDC69584BB507F2F3D765076BDA1D295BD328780BB7C8A043D7577503CAE67443D7761BC3CB513B2BBAC80F2BCBFBB06BDA751053C3399F33C2A792DBD7BBADC3A7AFB97BCEE02ACBDC20F1B3C58F2563CA1B4533CACD90D3C9C5B9B3A91AA013D2A8D013DB461543C580D223B88B36F3B1B64663C34CBFD3B7E8B69BC5786E5B9A193E0BCD6B2813C6AFA7FBBE67F2A3B786621BDEF63833BFA6ADE3CCEF9123C459B4FBBFABBE1BB8EB7353B9B5742BC7C99ADBBC149BE3C8FF8EC3CCD7FA4BB8AA20D3CFDCC23BD723288BC3682AD3BB9181DBC42F63EBC1C900F3C6E4D1ABC5A64AA3C87CD923A653986BC2A3163BB4D2E8DBA079ACD3B06EA9D3B7680EAB97AFA77BD2A4A7BBCB2C1CABB90543E3CFF112EBCE0777CBD6AB6E93BBDCC973C00D56FBC14B75E3B83F675BC2EAD1A3C9C1A8E3C2D952CBDBE64D23B6A21203C67EC04BC7F73F3BC6E13E5BA9D4C3ABDBE6F1ABEDAC28BBD6C39C13C26C50D3D826C063D4068A23CBFBB83BD482A273C2846FABCF9F690BC273A213C270A353DD5B6E6BB802EACBB7A19863D19EDFB3DC1461DBBE65BC43BFEB73ABCD0F0B3BC961EA7BB9D1B4E3CC028E8BC602AF73A786385BD0007A0BCE8C10A3EB7BEDB3ACA7F9FBBFF6E983BCB5DA3BC39385CBDA28C5DBA4E6CAABC5BC55C3ABF07C93C85E63D3E81F5B43D3D53763979DE923C2540A43C8683B2BC680B20BB6228A5BD789100BC64FF9ABCF702D1BD82B6F13D864457BD5FCC5BBE92ABA43CD67ACBBC232115BD96F2C7BC3085DC3C453D1B3D8E6C99BC994702BEFE57943CA9D830BD83C9C2BC7DD654BDC0B46B3D92706CBBAC82B2BC6477EEBB005235BCDE1750BC34DB923C095216BC85F496BBDA5E4B3B440915BCE661D73BCB722DBBA0B54EBCC80E61BA074A2ABA5B92513C1B28B5BB0DEA2EBC880AD8BB35EBA9BB6527193B0EE72DBCAA1AE0BABAAE86BBB686B4BB67FE3ABA6C8EED3C29FCF8BAB766C73B106F10BB9DA16E3BBE83873C446EDDBCD5EB1FBD06A0193D717A80BDCB4206BDF6051A3D912C9BBC2267AF3C37CE9BBB7BC9A9BCE05695BC9F7DF8BCFBFB3BBEA644B4BC0C49043C46FB3B3DC1CD9DBD7AEB8ABA6919313C04B82539C59A39BC7B5913BDB862BC3A82FC78BDDDF2A2BC1BD2DE3B4F412C3C9845C3BDAAFA45BCC4111BBE98F377BC59B090BADECF4ABC61AC003E4068A13D7D9BE73C4F4C823B4B419C3CCA30C83B24796D3D7977AF3C2546FDBCE8076BBD981B30BDD8D85F3C1B801ABB69794D3DEFC43BBD993C783CAC20AEBB1EF2D5BA970380BC5F5100BDD0F6AA3C66B6D7BD6591683DF814013B44D8CE3CE596D4BB79E532BC8E89CCBD2DB1703DCCDC413C8E462E3B8C90E1BCAAF8A33D4D629A3D92CA9BBCCEB09A3C3C27BF3C295ABABC1DFEADBDAB7B8F3B4AF7DDBC29A00CBCCA0138BCC32F163DA729C3BB8EE453BCBBFC8D3DBA94D63D69E8333CAA1BE4397C7087BC1762013C06ACC43B39518FBC88C75EBC28E63C3CC04E953BDDB37F3C4F5449BEDE5AD1BC1FA681BB97831A3B1101B43C436CAD3CBCE0013E365C19BE052AA9BC87075EBD923FE63C5367C0BC525DB73C5E21DABC4DC4A63D454C0CBCA51A0D3CD218BBBC474786BB8B32E1BCDCBC3D3C1EB5E7BCA318533C6242F33C746FA3BBF3A6253B19577EBDE5C8A33B502402BDB74421BDA2F7A4BDCDB82C3C4543D2BDDC4C87BDD0451ABE91FAAA38622BDC3CADB8343C1C2D5ABD4AAFA8BD435A83BE122074BC2AE4A23B96C14CBA6E184E3C462815BD8B03253D5B554FBDA7B975BB1A295F3DDDBC5FBB6CCDD2BDD792533C661272BDB1050E3C4E233A3D79BE693C491444BEBB8B1A3DB67381BD70F1A03C3BF931BE0DF79EBDD974883D577C08BD3134C13C5CCC3DBCA833993CEFF7AB3CAB61D8BCE790B2BB98EE9ABB0637F13CE190F63B139247BDA6854CBEFB147A3C9EDCB2BBADAED73BE937DC3C8C16403DCDEE633BCB2623BE33F8363C6856873C5828763CA771133EC152323DD4AAA6BD9F14BCBB86BC9C3C4A173C3C5E0B923B721C02BDAC1E31BCE16AB9BD316FAEBC56456FBC685CA5BCDB99A2BDFF287ABD12F33BBC67EC45BC8EFC53398DC75F3B5A3010BB175ACA3D6E5951BC1E1873BDE636463DAE5B87BC7CB5B03B96C7293CE0F500BDA269253CBE2D853BB0941D3B0EF1993D63448B3CD62D333D0DBBC2BCA7EC07BC60AEA23BEB5180BB13839FBC531A25BDE8EF263C67FF8D3DD2B2323C55D0F9BCCA8FE3BA84243FBCE801693DFBCC2A3DA22ACEBD3D535ABDC597013DD2EB94BCEB3C763D89340B3BFBB36CBCDD56443BD77313BE550B44BDE2306EBD1A1C8EBD187DE5BB9D31EBBC50F4423DFB8F0E3CCD249E3BC66C0DBAD99362BCD1621FBD40C60BBD1B808CBDA0A36DBD7AC6D439D5D6033DC31A0CBC85D1103EB535133DAB5592BCD3C6B5BC7105CEBCE70B29BC68C4213CBEC467BB37ADEB3AC0D9DCBCC6398D3C82C432BC9DF28BBCE25718BC668F05BD5406D53CE5BC35BCCF3E803CC5AE943C76C38C3C9585763BF79456BCDDC50C3DA472793DC0D7E0BC0A1E663C0B95C9BC8DB31CBCD95814396B81973C43F54C3BA878BF3DA741E0BB42F3A6BC99F17D3C4099253D7CCC55BBAC499EBC1B6538BE29D230BD7FC9B93CED482ABDAD65923DA0930ABDCAD128BD66ED4ABC79D88DBD020AD23B6739AB392C50013CB0FC3FBCF637B33B09AF04BC5754E53CC8E5743CD8A82C3D3E058B3CADD72FBDA10D5A3BA01485BDCB7079BC7245B23CBA2584BD9817463D4C52DBBCC3DB68BDECCB55BDA88A40BD7877263DCA8B8D3C7EF4B9BB078F93BB19AEDD3BB653ED3CC83C2A3D6B1509BE8C7D23BBCF725F3CBF6C863A2225033C6E8F2B3C08A516BD60FF64BEFCD7743B379885BC7170E53D5EDF693EC2AFA13D69C1A93C8B11003C48311E3C0F6F553CFB67533C14BA8BBBDE3465BCC284F0BC28177F3C60304CBCEF3DF03B9B96C0BD4BC28CBD98BE443C0AE77BBC82D276BCA98F6CBCD2B8F2BCE964383DBA94263D264543BCC7A3C7BBF62F4CBCFC44AABB413ACEBB7C2C943C54C1A3BC2437C8BBEC3F90B7F56E62BCE4671CBC6A3F0EBD918DADBCBCEF1B3C2425FBBC99A2ADBB396728BCADC415BA50A201BC77148BBCBC9E3B3CB256FABBC3050DBAE05E4B3B1DD2383B13C06C3B7A1F2B3DDEE952BE6B5C273CF914C0BC21C7683D4F129E3A0A633A3C278C8D3A22D2123D9A797DBD4C35D23CDE650ABCD354ACBBD8E52F3CD44496BC71D9EABBE4B86B3B029960BC4D22EA3AA8E0E33CED3BC93B272C0BBC427B74BDBB6CDBBC958BADBD48B05ABCAEB4C33CD9391FBD80A6C5BD8A0999BC76DA053C948B77BD5295293E4847E2BDF9DD8C3C37D215BDC8DC093D988E9C3CCB1BDABC55BD00BB173E9BBC6CC440BDF3F3A23C8D796CBD96F8B53B14DD9C3AC05D19BD428314BD642026BC55A253BCBCBF963C39C9C93C0C8359BCF8E7EBBBB4495EBD79DD913C7E5CFFBB2A3B2A3C0C4B943D7ECF6B3C4D7932BD5A4FD9BD131C97BCA196543C78A7B63CF10C17BD2088BEBBEADCECBC45FE843D5CAC48BD3B7C42BC5E1829BCB8F2BEBCE8EDF73B37AA9FBB18D2073BE65B8DBCAB0E643B3EE1143C846685BC6F56DF3D64A79C3D0092B63C8AFC2E3C95207EBDDF115CBC9CAEEF3B6DFAC43B24BAE33C11A230BCAA3D5C3C01B43C3CD48B5C3C4A44EBBB945C473BF4E50CBD92E82B3EA5EAB6BCF1A10ABD5CB0143DB313153C25E3173D790515BBED26F7BAE71925BDB0269ABB8A2949BC08A40BBEBFD9283CA52D4F3CBEFAC7BC147D00BE4D76E8BB8DD0E1BC3417A8BAC74BD53BF95920BD8E8950BC6FF2B3BC29986D3C0EDBB6BCC2CD5DBC2B74503DF31F57BC05E7C6BD9B679B3CCE5B89BCFC308FBBF08EEAB9222DA3BCB0D73DBB563700BD62E2663DB1C75C3DA26A06BC206DFBBB07752ABCFBE290BB7FDC03BC78E7733C44C7E9BC6CB6333B73BF4B3CBA1378BD721DD6BCFEA908BDEA2C39BB9CEC6E3CDB2507BDEB16C5BB2CA3963B313E053B72B98A3CF90AB8BC6D4E1E3C84C0EA3CF64DAE3CA6C6CF3C7CA4103CD7EDF9BAECA447BC0BDF283C9585553BDCC1B13CC864C3BC237E56BD1BF19B3B2B3D6ABC1F85DEBB9B23B3BB8AD29EBBCE82D43C88263ABE962CEFBAB1401B3AFFD98CBDBC9A5E3D5BA31EBDC15B1FBFA3B448BCBFC1D33C2BA2A83C5DC2C93CAC683ABCC6A34EBBE09E673E41A687BC6FE512BD4561863A51FE723CB56EB63C5905833C0DA55FBCEC67713C20E613BC10244F3CE433B43C8B1BA63B6B06B23D240C8A3C679C243CCD0D97BD786B863B4C3D803DE20576BC7B4E58BCAE76EBBC29CD9DBD1B15A53CDD0BCCBBA71A353CB1219CBB9A6F533B638E12BECABEC63C00AF4CBA2D7866BDFFA6CE3B6F4353BCA07A03BE9186943CECE8DC3C3F9DC7BB65DECDBDAB6E583B97B3FDBCEF3682BCDF7F823C2C684F3DFC4466BD33CE103C020E803D6530083DEEAFD93CE06506BDCAB756BE0C53003D33B3E43BD80891BD0481CA3E32D9D5BBA5A3153EB733EEBCF4BF00BCC242F5BC49E024BC52FFC73C6879173DF724EEBB3EA54E3B780E2B3D775991BD4E96C3BCC6BB47BC60AC453DA52DFB3B7B1F003E6D4F9A3D19F06C3D298ADF3C2BBFEF3CF7161DBCD477FDBDBA90193B5E6B89BD2968173D0A7F28BD09287F3D422B6ABA5E5B053C1837A4BE1A8091BD26C1E43BFC0D4C3CCA1E9E3CB6D40BBE79B5013C93C53ABDCDB79F3C4E2ECC3C5482FE3C571EC83C3BE49A3BA23D433CD4D9103D5FD9A03B42900E3D49003ABD262B03BE8248023CE74C723CFC481E3C5A94573DDDEC5D3C83C58D3CDA7ABF3CC6C455BC84C342BD1552F6BA76A0F03C3122EA3917A1B5BBA642A93DD4E9033E0F0B22B97442BCBCE2778FBC79C4683CBFC56DBBE73BAABC054EE5BC5A42D0BC61416FBC53DE77BCED6DA33DD1AB45BC8C5183BC3263A23CC59F4DBCD22B503C4C3F843CD3EC483C49F4333C0D3BECBB0AE1F1BC40DEC1BD5D56303B043005BD0E1480BBA6663E3B4ACCE8BC047A193C7D6D8B3ED20A313C04D642BCB984083D5A161CBEB1C9463ECC51F93C775E583C29296C3C68AB96BB906770BAA1963EBC2D0707BD3D0F54BEC7CAA83CA65585BC1B22053C6C047D3DA3C4BB3D654F383C42A0163E619BA43D1A6E2D3BC11BDEBAD4E1173DB93EE6BBF57090BDDA7E513DD45FA8BDDA35DCBC68DD96BCC37A923CF24E84BB8BC8E03B6C61793D5257633DF98134B885C72A3C578427BA2561A6BCC96F2A3C1F7C0BBDD2B3853CBEAB303CA2FDCB3C4A86E73B960872BE9DF89EBCE0F1203DB7551ABD7815DDBC39EA303D92025F3D8D82413D50B153BB01A0963DA5B0853BB014EF3DCE21B93CFD2506BCF7BD4CBC3EEC8ABB52DC17BCC698043D79F1943D9065243B9CD0D23C083899BCD9AE7DBDECF721BE0C997EBC69AC4FBD4A9146BD807FE3BCC1FD2F3D741475BCA05DDBBBECDE55BEBE5BA23B1F75A53C01A1113D96C3AE3D2EA6FCBDDBFD133B8EBC6BBAE2B9713D3631A03DD19360BC7E1BAF3C5D44203DD164953DE09E82BBF1F7843DF02CE4BDE05C6F3BF11019BDCAC0073D1FB90D3D3F7735BE7508D83CAACF373D462DD23B8204C6BBFE704FBD1E4486BD8648983C2A7807BDA952C4BC7A2F163D3C3487BD0306CBBC1B6ED0BD198C9F3CD7AF1B3D30AB39BD675EE8BE1A61893C288965BBEEAFD43CD4B78FBCE8BDA9BC9FBFF7BB8D74C33C9F68BFBB74F7A1BB505171BB55FB2FBC6AE0E53BED38843C7F96AABD9B680DBDB4EF11BC320EA93CED1CBEBCB27032BB25390FBC994D5ABB710100BD96BD033C15FEBBBC7F1220BD560D54BD2AFB503D6C17BEBC7D11F0BD78B5903A77E90BBB06049BBEB056C3BC50F84E3CC5CC2C3D361CFD3C56EC0C3DAE7063BC4AE352BCE45DCC3CECDEAABC26F8E13BC2469DBB9BB2023DFD4D2D3D195792BE276B733D8C7F3A3D7FC3ED3B774890BC87E240BCAE72243BF0BA7CBC3A3A93BCCCC4F239713215BCFF9BC6BC06EAECBC2F03F8BCD65F3F3B70F697BBC0CF553C102805BD0EA40A3CB1858DBCFB5B4BBBF3474E3D83C4E3BBF5449DB9EFB94F3C2A7E603C8F3CF93B42BD883C618563BD1DF3AF39D5E9F93B17074B3DDD28933CEB465FBCC9DC3E3B9C98853BA8573E3B13C2F63C187E3F3B35831FBD454FAB3C5CF69B3B7531C73B1E09DD385DC1EA3B1C9BB53C0697F3BC413CA7BCCEC8ACBBC77D76BC4934243B9BF6523DC230B43CD27ABF3D9AAB823DC0F7E73D350DF53905D81DBDCF1BCDB95DA2023D6B9A02BE7A0482BCD7ECBBBB17E7A2BD2FF7403C04F1FBBDE951063C5D028B3B5D1FB43B5382673AE4DF76BC28CA693D52778D3DF5B58BBC6C7D923CD3FDFD3A9AE573BD9C4A923C402713BEF9E7D93CC7CF463DEBFB74BC7EA54C3CE822A2BDDEB4BCBD3BE0D63CCF0CF3BDF452F1BCCE193C3D0611FEBD0931CABCB88CB5BD04BE2A3D8D9EAD3C1C2307BD2F0778397DA0223D94769CBC51378C3B3A36A6BD3CDCC1BC9D9309BED48A273DEC9AFABD55870BBD2E72313C82BBDA3C8A88053DEA438A3D5D716DBD03D7CE3DA18A8C3C457339BC25B3323D0909C73CF75A05BC8769AB3C2A781EBC9DBF133C82D8113C7F6C43BCCE56183B30497B3CD86234BC17902DBD693DDD3BFE4B87BD551525BCF2AA713E359B333D35052D3CA2258EBC87254BBC422D8A3C4F5C8BBC63E3433C9BCD0C3C4540573BABB0293C070B07BD5393433CF38AFBBB71176ABA1C84333D1858973AB3FDF53A4775133A825AB93DDC1B2B3DB404AF3D6C3EAEBD35A8FF3D9EFC7FBAF5949C3B392DE13B6DDF3ABE5FC41E3D0BB830BC50640EBCF4F239BD73DEB83CB5C625BD33FB8DBB0366FB3C7A8730BCEC3357BC36B16C3B699AF4BD893BA23D1AE91EBC9F802E3CF1A7FC3C941143BD034BF23BF00853BD9F25083D5AF913BE5D04BFBD46B4303D371DE6BCCBDE4ABADB3D06BCA7A10B3B3F94753CF1C090BC1C8CBE39A9001ABBB5D2F6BB823079BB6EE5033A20715F3BEDF3443D1749B93D0EAC313C2A46D63C2EAB113B85AEB93B911E7ABC0088D5BA5DD25F3C1A89B7BA625921BC176F57BB7C75233C34F30CBCC0C3493D69D0D23CC5B501BDB1D3C9B9326E0E3C39E317BAD994533D98151C3DB9F1C23DF030CABBDE4508BD3CD13A3D25FE513CCC67A4BB3F608C3D901B36BEE75445BDCFC09E3D591840BD40368A3BDD9983BE606FA93B86456F3C8967CFBBB443DC3D0A76073BF70453BD83AA29BC0C682B3C7187FDBC0C0D183D0023AF3C00CD1ABD1A72DDBCAADABF3B5220743DFAD98F3DAC5CF83D0D22803B8F774D3C5D78723C9B6F71BBEBA836BC04A0EABB276D993A9189C93B868CD6BC228449BC024A1E3CF9E0F4BCDEAE3B3D872D73BBC0C2C0BC6497E83CDD22D2BACD72A3BC89C46DBC75C2D7BCBA1A68BC0CEA96BB65F0F8BBB6B84D3B6E26AD3C26590ABDC9A3453D2D3A15BD60F6083DB35C663C4263CEBCE87EA43C21C1643D52F9AF3D3C5C953C650F99BC5C44D33DD83410BDEC8FC43D69FF6ABCFE2EBEBCEC813A3CA6F2C9BD83A9B03CB68687BD26A6BAB6511ABBBC042C1DBD044271BCA1E2E03B5B3EAC3C3417273A51BCBC3C1F12D43D08E757BC16C41EBE336F95BD4EF4B33CC544673D3F55DCBC84FC72BCE82AB5BCF5DE83BD57ED8A3B11B3D3BD15B801BE2FA597BC985DFFBCF90051BD06525FBD94CC233EA307A43BED8739BD60A81A3D43EFCE3C6F1F763CB4504D3D2A15253CDD34143D4374B2BBA73222BD110108BC85B339BE9D7499BC0020AABC2DA5B1BD15DFCFBCA7A5F43B9FA494BC2B642ABD2895403D6A0AD43B78B8603BE54617BDEDC21D3D39AF12BBA582053D4FA6E1BC61C419BE76F3EA3C37AF0B3D11CD8A3DA40BF4BD5DAEB2BC40055DBDD3F6343D6AA23EBD808D703DB9EC39BDD0B3043C0D5310BD4D764BBCBE19A0BC8307333CFB4762BDFCA91CBDB50BD53B6916453C20F42E3B80E01BBDE68727BD7AE927BD3CD7633DCB5B67BBF5CD843CCC79A4B9D0CCF0BC2C7C4C3C1CA1EBBD02446FBD58EC37BEC42E06BDF7AB2EBED944273D6FD4BCBD2817BDBC2219FF3C4D6E483D81EC0DBDF23F283D9B868CBCE3B8073A6FE6353C98AD15BDBAD4A2BCB77EA93A1C5C87BD3DC618BD3409013D8155DABC130A84BC8A5DAABD02B1923BDED045BD2FC9FBBB9807A83C73F6A63C2B8C213D613828BD5B5B103DA261F8BCFDB0083DFBD51ABBDEE8C4BCC0377D3DF95003BD110A26BDD37DB7BA41870DBDB41CEFBAE70B613C261A35BDEDF8333DCB770F3D4020253CBB82963C6E3C6DBD0AC6693C74B621BD6913B2BCF760FABC11561F3DA3F2893B65C9613C88B41ABBD020833BA94F093D25964E3C29158FBCEBFC69BDDD1987BB41B70E3CE26533BCC574743A2826233DA89D02BC6C38D23CE2B1643B5F1ABE3B8ED4BE3B2BE1803A4110DCBCA226F7BBB6C893BA29F8D23C471A99BBD4703E3D8D1CB93C0DFED2BB6A2DFC3B0C70CFBB681802BC6FB3083D275EDFBB9E5901BB324B8ABC3B0462BC2FE0E2BB96853F3C10EB853CC1944EBDB2BDC0BB9D4C3B3B2A1CAFBBE76E9FBCAB31A33CFEB907BD4DE302BDD3593D3DDD2C623BA1998F3D8A66B5BC7D8E95BD5380C0BB904983BCBC7B303D7DAE883D94CE8BBBA0849F3C1EEAAD3C9384163C8872353A6348BE3DC31F6BBD439131BB5525673CF6BD11BD231CFFBCC0F774BC654BE0BD68AC863B7E5E193C154024BDFB2516BDDF6A10BE16CA45BE9137673D6DF21C3C5B77833DE6A724BCF0806BBCE4733ABC6781CCBBD2F409BBA09514BC1F3035BC71D46F3B912CD53DD939C1BCA5D3B5BEBA9D92BD844CA4BB25817E3D74337CBCC520933C7381C03C380D863CA285E7BB9645AB3B79341FBC160E78BDCAA449BCC86CE83C15E007BCD2CCE0BAD2F51DBB1BC1033DE7230B3BB261EBBC40AB713CF0C9B3BA2C30943BC5DDC33C1EA5B0BC42C218BD5EAB63BA37548ABCE8E86F3CAAC3883CBA601ABBBABFEB3CC57C813DB8B4573C15F7883C7A50DBBC46238F3B0A7907BC8B6A26BB5A62783A4C9B3A3C4F7F3F3AFB21FE3A222A243D0E9BD5BC2E6D083C8168643C6868A2BCC42C383B0D12903CE5046E3BBCA385BDACEE7ABD9CFAD03D71020FBEA40AD1BD9C31FFBD58B3EC3B430495BCE11957BDEAF9223D8393F9BC12DD94BC75B599BDC303513CFA3A22BC500A1E3CC596163DA704F9BB4945A7BC3DD30BBD4A0BAD3C1C2103BDAF2F1A3DB9B148BD156A333D9B379CBB68EF4C3C9F8F50BEAE12CDBB68D806BC9D65C6BDEFFA73BC7D84F0BC1DE5F8BC7CE2A33DAB3817BD8E35DABC7467D9BC92FDFCBD8F9B773BBE8E853A631706BC0AD3053C86FD573D450E3ABD0CE2A93CC77946BC69B6E1BA3A01E839E6A7CF3BABEAAC39C64C7FBBDE70DC3C6EBE40BD801FFDBB7039483B9ACFF4BBC9E96CBBFE3F0F3D62000ABCF3B2B6BC0D9B2BBC1CA9593DA463163BF282C9BACDC953BD7D2098BDB3532BBCB4A1983CF42788BC90909EBB1BCCF7BB40129DBCC4D4B8BC89B20CBDF47635BC4425FE3C2D13B03B0FFF6E3B9B5042BC39923C3CB7B75C3C03DCC0BDB5C5C8BB3F2AB2BC9CDE8EBD5C120FBDDCF168BD9DF501BD298ED1BC0E0FA2BCE3D8303DA47B0DBD77E1E9BB03ED033B7EA9B03B138BF4BCE32100BC1F179BBCF9AA6BBD8A9A143CC9BF94BC0DEDAA3DAD2EE8BBE3E254BC51B888BAD2C102BD3029BE3BA376623D7D84303C82BA423C988875BCAD59143CB28B81BCCA50A73DF5FD5A3CA6F98F3C783147BC0C86C4BB074BA4BD12CF96BC6913F0BCB6EC113DA69824BB1344B1BBE0ED96BD9A8C94BD0B2D75BC9300DABD8A256FBC24028E3DF2F1C8BDB65D03BE7452AABD05A4513D98BB1FBC836980BB45B483BC2979E6BC7C58D2BA30F88B3B04C28EBC4951B33CD3A1BA3A1197243D6B8732BBB2DBC43C1C9B1EBD0096D2BCBE2255BD35D782BDBFD53BBD6BE5DF3AAC4EDBBD988CCF3D53B345BD2E0A3C3CB9048C3B55ED89BD7C7D84BCFF7AC0BB054C91BC40AB143D429085BC6A37983C137FE83A3844403BCDFB743CFC369F3CBD99B2BB2401133C53D8503BE3EEB93CDFDE5A3C8020033C9774CC3CD503483B895FF6BB30D114BCBA2811BC03E442BC5E96B93B24D7B0BB41A6473BEE39023C1C121EBC3ED7ADBB25D48BBCBD178F3CF7E5B93C5DE22DBA78B4AF3AE05A573D42D4953C372E313DBAF0073CAEFFDE3DE082FD3BA0EB2CBE634974BAE55444BD27FB9EBC1103FFBC3621C23CC70DE1BD4F3B39BCCB8E1EBD06CBD6BCB24EA4BC16C31D3B59A187BC4811393BA344ECBC24F8CF3D4A1EB1BC069E98BC676263BD8686E0BA58E9FA3DF3E462BBB0001C3AB7378FBD74C673BC43DDEF3871D904BE10E202BEE1F93A3D50049DBD09744DBE52BBFABC565228BDEABBA8BC6A38513DE09399BBFD2701BDEC52583B656D37BD183042BC1E7A2C3CACE9093C2604903C5DE1CAB9E1E132BD4979E13ACDFCADBDCB17ADBDE81ADDBA70EC8DBD37DB8F3C40C0BD3CFD24093E34F4D5BC82A57C3B6B59FE3C1498863CD099973C68A190BC2A12B5BDAF10E13B08A2BD3CA81E653D2FFDFDBCE318CBBC9D7BBCBCE985BDBCF400B2BD15847A3C00F31A3D722BD0BDD5F83DBC780C823CEB0E79BD9F777C3D5979853B2A3EA0BC9BCC033D50A8123D9EB07ABC3B359FBC9A4C8B3DB1451ABE366EB03BDCD8A93D0E794EBD8120F339D518B3BC754FBDBB1BC9BA3DCD6D5DBB4263C63B31D68C3AD9CF4FBC6008D7BC65D247BB4F7FF43C9D699EBCBB14B73BB73CF0BCC577D03BE773733CD93D833B8671633C1FA43EBDF76A22BCCCDFBB3C26C184BC58F18D3CFA1D593AF55B233D2A6F03BC7F45033BE06F593CB325743C13D69DBC072404BBC003DCBC37E392BD81407D3C93056A3B0F7A183B87659E3D6CC8E6BC7CC63DBEA83BCD3C93C0683DF814DB3C013AFBBDB85B84BC8EBE5ABC1366F13CE4EF13BD6BAF37BC915ED9BB49A01C3D45B0123CA746ACBC821DC93B2B0390398D9DCABCFED4A3BCCAF8C13AAFE3C03D1888F2BA72C094BDF53167BD2DA32BBD138E11BDC63B623BF3341ABC5659853BEA2C85BD8D2550BDDA9F4EBD32F1CA3CB104493B32B21FBBA83EA43C8218BCBC07414CBCD2CF7B3BACCF78BCBEE8A33C1B67323B64F9493CAB245E3DF15117BC77B87C3B2369073D51147FBC1C8D21BC74CDA8BC0D9595BBD7BF5E3CE111E6BBF8D9ECBB43B1083C4403273D1AD16DBD9AF3773C580587BC5125B3BDE29181BA4CEA1DBD0FE5B13A91AEF8BD0E4BE3BD69F0193D99D0C1BD4770E6BD584886BD954386BC436D05BC49EA6A3D6EF7A03D71E5C4B9344FBF3CE13318BDACF1103A42756B3D7DC7FE3B4943B9BD259B163CA32436BE87D8D43C4844C1BB8103F4BBCE1072BDAB581DBC027084BC907B613C28360D3E4263ACBC5E8290BCA7DFEC3C2443023D9A700DBBA8728A3D3A0805BC751760BE65649ABA918C813D32DC3E3D985CF1BD3C7772BBBF42403BC37FD83BF2292FBDC8E486BC5522003D54FB1CBC6AE4ED3C84881EBDA2AD01BC8419603B32A660BC5F5854BA9BA931BC7999AF3D8297A9BC712593BDEBFDCBBDC61E29BDD9E723BD0D8C813BFEADB4B924625BBC2D4289BD9BF4E3BBAB9768BCBD66BC3B5DFD983CF3FEBCBB0455AEBB09B5A93B3BB7483C331DF93B30ED8BBCD4AE35BB8455A13B49FAC93AE151C33CD6E8103DC7E7763BB038F43AA649E43B0139733B82477CB98285CA38B94EF2BB3C57123DCC12C43B4DC068BA8AF2533C2B07223BCB0E753C366B58BC846F99BBC5E6883CEFB9663B4A92383D9207CEBBD3FE55BC1C8CE0BA2A4283BB4BCC1C3C63118BBBFDC89BBC4F0B553B3A5D38BCE1F727BC7E45733A86B9B63B46A30A3C32051FBC90CA42BAFB39753C040406BCDDB2923C6F2A07BC1B82093BBAF684BCF110753BC275C1BB3228C7BBE2FD9F3C5EACE9BCB4D3A73BDF6A533AFCF991BCDC44123B5DA185BCF3DFBABC12A1E3BCF611A3BDEF639BBC5A0518BD9EA97EBD7ED4FABAA8EB293D7420E1BA5C280C3C8112D2BC4965CEBC3EA535BD879A7F3D6354A83B816E6EBC68A366BCAEF9123D4B2511BC7C01E4BD45D898BCD0893BBCF1340EBC0BC36ABBF82127BD034DA7BC5791803A3ADE00BD860E2C3D1B0B693C585EAFBC5ED8923C6EB30F3D7281CABCDC0A88BC6964DF3C241B0CBD57DFCABC69043B3C1100AF3B49D812BD4EA7DEBCC4F8AF3C4D05F3BAF9B925BD426EC4BBF2236BBE35C6B5BD283BD13CCC9409BD8B9F71BCAF13AABD9B53F0BBE4FB7BBCD719923C1F3504BBBEE0BBBCE8DD3BBB2E2E09BD5C38C83D8774D9BC8884F3BDD2BC0C3CDACB8CBD437F453BCD963EBBF9862FBCD6A751BB2CC640BBB98FF43B3BC12CBC8CD9FDBB0587BF3BCE5FBEBABFF31BBC5E13B1BB4FB612BBD52239BC6FA48E3C37C3A33BCBBC403A90A5B0BB3E81313C95CC133CCFDE25BBAD44963CC31D5E3B7CBF883BA12605BCBE7C65BB0D2FEABB87BE9D3B3F71403B860038BCD30A8ABA148653BC066C143C82DFD23C3973C43C4FA1E2BC832F08BC95C3663DD796AABC95CF983D298BF4BB100DACBB6BB39E3C3DFD4ABC8C00B53C4327013C1D46B0BBCFC51B3C4BA124BC986453BB6882A13AE2A3BA3DA8DC05BD36718EBC8A5DFD3C1663D1BC9C8B74BDE618E4BC37348EBD06994CBCAA17093C2F62CFBBD7A4A2BC34E04CBD9F24B5BBA77EA0BD4F0714BE1EC08B3CCF28AD3BD5F49B3B1BA9B7BD5621AABDF9A5C2BC37B65D3D1C3C893CF280953C9B2C07BDA3C36ABC5C0A053D490DC9BC5261B23B8B16EBBCDD01033C71FA40BEEAF637BD6BE0FF3DB23BA4BC93C5C8BCE1AF193D85C73C3BEA0A38BD44ACA8BBF333C9BDE29B083D147EC33BAC67033D92D5823C03A02ABD5557DABD3876EBBC150759BC2B6E92BC731607BDCEA8F33D238CB0BCD66ED5BB5A1754BDDC702FBC8DAD9BBCA5CA373D64D233BCA49B97BB1D85A1BC0601D63D0BE6B9BB060B0FBECA098CBC053B37BD87B4BBBDC67361BB784C9CBC0FE114BD610E19BD0CA50D3D195ABE3C707A42BB266123BD43F1ED3C0B86F53AB26AD3BDBE8EA2BD868C80BCE52278BD27832CBE77181CBD5375B2BB89AEBEBC8C9B8E3C6F6CF4BC4928BCBDA99A7E3BACB339BDE07949BC8397FBBC437D2DBD3212DBBC2E30A539688B11BCF09D6FBCD31F1B3C6E170FBE51105EBB827F16BE5716BA3C07FBB43CA8586B3DFC3ED5BC6FDCF6BB4D76B83B1B1A11BD8CEF8A3CDE27C73C2E214E3D85F1C13DB7A6D5BD834F3F3C298595BDA7749A3D0F8E1A3A6115C43B815F903D68F194BDE4A3683D4B3F28BE0A13C2BCDAB82CBD7DA543BD7C144F3C90FE823BA3B8723DC38C083C9E69523C8FE08C3B6F228C3C1BDC4ABDC661C63C9FAB0B3D377D6C39C4EA03BE9AD4B63B018CCABD066C9E3B08E59EBD82C2EABBAACB4D3C9231433A1CC3B83CC23BCD3C62D255BBB339013CCD7B08BB02F0953C09A0A3BCA5FCF4B78BD490BCC5B102BD4103173E3FFC1D3D27B0F3BB7E06C03C4F8602BC4EBC0F3D18B4C4BB04370EB9F4816ABA824D9B3C5F61B63CFA1FD8BCA926893A905E08BD95B22ABC8E67AC3C2AD77C3CC584E2BBD9745F3C92A4AFBCD6E90CBC2C52A6BCB7C99BBA4C65D23CDF6BF93B539063BC8C83503C6EEF99BCEF7B7DBDE0ECD23B981069BCE3DDEB3B6116713CFEFA0B3DEC84593BD4DADF3CC58C383C44250BBDFD8989BC3C18B03A3BB6653C32F0B8BA68E929BAAE6AD5BCC35F7DB72C4EFF3CC2C1E1BC3E8B76BB7A78C8BC8E6E8C3CED72E4BBABD3D93DE85A403D922A283DA3263ABC40A91E3EF0E65D3C6DC2A5BDBDFFD8BC0C82E2BD03A7543CC908C1BC9FE5013CDA34BBBCD0682CBC092B91BDEE55873B0BF6523C6C7D42BC3F8186BC32E507BD2EB3C5BBA7599D3D931C5ABDC5CE1FBA6395A4BAB165FCBDA926D93DDD6D57BDA19E39BBA50001BDA8E68BBD3052073D57E255BCC5F7CEBCE30E203D75076FBC0E71FD3C307C853A761EDEBCA8C435BB75EFDEBB4B3FAE3B24B273BC684C1B3C25BB303D52F11C3DC94CEFB7EDCC3EBB04DB27BDD05C3A3DA7B24F3CC3EDB53BC6B39EBB3839F4BB62AA22BCE3827BBC08C1E5BCBA2BCBBC5085863C59953DBCDB3D1D3C9D71923C45735A3CE57BAD3CA89788BC16B12ABE986CE9BC44DC57BD8FD1A0BDCB7694BCA9414637831F00BD4A6A83BAD28B8EBCA400CC3BCAFBB9BB16ED01BD140C37BC0E0F3B3CF9E806BD378EB1BCA645E7BB757E1BBDC0ED513BAC4CA6BC767B07BEB5FF143A0145163CB26E17BD0393883DEA09A23D9E9F1ABD35A529BD596ABF3CF08A993CC2D819BED16523BD8673933BE0CC2EBAA4D701BC1C6DF53B9DA9A6BC3BB76EBA1B7B5DBBB951EDBB65FD213CFC51973CFA96FF3B62BF293DB2C8AA3DBCCD27BDEBD12E3DB54814BB799CCBBCAACCFBBBCA28F3BA4AD7A03C9E6A4E3CEFE761BB94FE843B8D17923C81B430BDEDF2703B84E732BCB5ABE6BCE1B5B0BBF5A813BDD121BA3CF0E488BC4FF28DBCF29E5C3CC19412BBE8235FBB44CA27BC21760EBB094FA4BB978EC7BB07FEA6BC3D2A0BBC3BD63F3D4AEAFCBA6A8BAABCF4ADD93A74262DBBE1C0E0BCFEE3153DD53190BC8173FC3A72A4263C00CD64BA6772C6BAB49C2CBC28D723BA411194BC4611253C163F81BCDD2250BBA0926BBBD1F44439B59DE4BBA968D5BD82A3BDBDD5AF39BD34F661BD6E1155BEE9D7E23CB340E1BD11E0AEBB846D763CEAB6093CBA9F31BD5532263C74451EBD2F6CB13B739492BC442FA4BCFC4BD43BE86256BCC2EF11BD48B262BC84D3F53C286F98BD9D8AEA39B88ABBBDC840E63C2A7D233B7A9DBD3DD4E5223BFBE1B83B7441BF3CBF149CBBE2FB04BBE77A85BD3D12B5BD85304DBE32487ABD4EE210BE9279A93C217891BD24658BBCBABA473C0ECAA23C90ACFABCD4DD0D3C9BC5DBBB4EDF0B3B02243C3D63F749BC376230BD92600E3B87F056BDB227E8BC6FBA6FBCA165D9BC5AA06DBC18C99CBDB456063DF7F046BD61AF2BBD2DE1363CC63784BDD37C233B87E441BD45C08CBDEF1E123B797BDBBC62460FBB07F16ABC38B3B53CD07E88BB11F3C4BC02AA1DBCCEC106BDC2209ABC4A432CBCBDE2373D55D54E3C3CF36FBB89AE05BBED44013CF36951BD8F25623D24A27F3BF2AC933B2500D83C5768A23C0F7AB73BC4E4CABB6A530ABCB99C0FBD0AD14B3BFE2205BC1EC7C0BB154808B9A650F6BC68DF45BCC75A4ABC255A7EBD76692ABD753C82BCA3C3143C37DCA4BBE8CBCBBCF974C1BC960DE03B8CFFFFBC426B7FBCA31B40BDADA9993DB823973C1E11503D58C74FBC811437BCB9A76ABC92E58B3D4C2CB63BD5A0B0BC9712783DE6E9D0BAA3B965BD880262BCC0BC2DBD512FC2BD26B9173B3FF51BBD4AA380BB44C48BBD82E9233DEDD6633BBC8C6EBC6E8546393580633BF607B53CE02B29BCA272A83C37D417BC0C7F0DBCD0FFBBBC9F8F5DBCA4C99D3DC9D9BFBBE870A3BDEA8821BCD121AEBB6AD9F6BCA0FA8D3D4C6D023DE7B7913968FCDC3C4EB08C3CBCCDBA3B3AC0EEBB881D6CBC79B98E3BA4D4D63B4777C0BB254E5CBA058231BBF45106BC0DF1023E89EDC13C6F0A6D3BD466C73C7EFE203C1E02173B6E2716BC29527A3CBE6B263CF3561ABC32C9863C583B103BB7578F3B2871D6BC0616943D03AF193C7940CC3BF8532B3D5DF3CABB28D4B93C9F4312BC841FBE3BADF6903BF917923C03459CBA637B54BC56B5E03B880063BCC0278D3B48A7333DA8004A3A60C8D33C90606DBBEDBB303C52DB22BEE24EE93C846E393CFACB2BBD2EABEABDB3CD2ABE401F81BC8774433D889FA1BCA5BAFCBAB9C01D3DE7931ABE7BBE8B3B70A6983C7F8C68BC752632BC39539F3A7FF5963A67B7243D09E3523E8B8F94BD865B063C476C203DACD4D2BCCED4943CE462873B817E29BECF69F53BAE81793D7DD9FF3C38E12C3D7355EABD96662A3CC986093D4BC518BC1F8BDA3BE5B91E3D705784BD019A9CBC850D1DBD6FA3C73DEAFD073D39D14FBE94D4543DF90DD53BDA9A32BD1FA20C3C3DCC7EBD91FAACBC7EBE42BE97E228BD3D06883C1F3FC63C460B31BC5638E9BA06843F3D968111BD6ABFCFBBCDAC4BBCFA291D3BF406583DB31F18BE48D6AE3C11136A3D9D63B7BDD2B0403CCC4DC0BD69A9B53C5DA9173CE79F44BDB48F25BC5BC690BD6321F03CC1D8AF3C7AD0933D1B9B2FBC9A32ECBB4B71CEBB0A4985BC8839023E730993BB6F241F3D748EB33C22E621BE03D004BD9CE7083C51D89D3D629A8DBCD5062D3CFFE00C3C56C39D3D93B4033D112FF9BDB1FA383DF6F3D7BB5C3676BD2A95AFBD4E92BFBBBC852ABDE08F04BDEF9D4EBCE6ED1D3E79E2EBBBC00CC4BC23D0BD3CC0B2BFBC2FEC79BC4CEB9C3DC83F9A3C72FCAC3CC84A8ABCA34C893D9D4B91BBA4190CBEEB108C3CFCB4893C150080BDF1CD9DBCD0C70ABD11010BBDDEC38B3C8460303C799F683DE55A02BD914DECBC6224753CFEE222BF8809803C684B923C0EBF9E3C267C1DBB766E0D3C6AAC05BC647F333C111EA6BB55E0E1BA78E1E6BC15E74FBCC8BA9CBD4D0AC0BC12A9B63E24E802BDBE55E6BA548A903C0A2AD5BCF4F6CE3CCC6EFCBCF846C93CC9562ABCBE526A3CF3F4DFBBCC1ECFBCF60872BB4C5020BDB162DA3A0629C1BCEEACAA3C10C72DBCB161E8BB042290BD60EDFCBDF3B00E3CF0A781BC4E9A873CC6B662BDD123183EEF5F7BBC0B7884BD372A1DBBB9CF45B827E6883DF80F1D3DA41C50BC7CDE0C3C95E08B3BC9B221BE50BD213C7A6A55BE5761FC3CB5D812BC7CBEF8BD210346BD2923B63C034FF4BCB6B8B9BC3582123D155B9FBC79B019BDA4407CBD4093803CCABD67BD7C5E343D5E8AB83C4F17153E56B299BC54D62B3CAB352CBD6EDDE63D54815EBD8880B63BBF4E61BDF74B283CC7B3013D394283BDD595BEBC37D925BD26F1FABC887C1BBD9D5D56BCCF8E0B3DA95456BBBBBD3ABC91B6A4BC86BF4EBC5F6C093D81AFB7BC2129A2BBB6594D3DAD44B0BC7335A9BD4D4A8CBC45B93A3C172A213C890991BA753BB2BC4F42803BE8957FBD670A84BE3740B9BB7B1180BDC1ED09BDBFDDFEBA50BD623C7B86FE3B614AB83C1ABCC33C685C2A3C89F3D63B165679BBA2030E3C0B795BBB1B7CAB3CE48A09BCBEC2F93CD51D51BD70261BBD956C18BC0553093D3EEF653CE1714ABCBB638DBD7F8C853B909F8E3D87BBB2BC02D0243E25DCD13C5B3582BC131FBCBCD5911E3BB627AEBBDDA31D3BB5D729BE070005BD333C21BCCD779E3C7A628CBDBFEC67BCCC685A3CB6320F3C06867CBCFC0309BC036E543DE24434BD1AB9AABD07AFA3BB241CA4BC0C6E1D3C23EAB13C23A60D3C6D08ACBC2DB8A9BCBA7626BBA4BAB73B3970783CACD32D3CF6C6C53B32E344BE58B01FBD0556BEBC6EF8CDBC3538573CDFE2CC3C21AE83BCC62FC63B3354A33BC67109BDEB56B73CE214E73DDAD736BDA93A80BCF9D930BCE5D88CBC611FA8BC17B26CBA8B3A653E6303883DAE63B73B9D2218BB105A4BBD78FFA03B94D951BB45DFAB3C3D3E38BC821E9BBC45F70BBC5F6288BA4E402DBAB1FEBB3C01DDC23C5A0DE9BCAA1FF8BCAFDD53BB47A0F9BAA46F773CA52C223C833B83BB421CC33C307CD63B555A0DBD4908083C5B8924BC9E67CCB9309051BCA96802BC936F69BBD64782BC6FCD0BBD1424E73BD2750EBBED7959BBA70562BC13BC703B32DF673C464B913C02ED5DBCFA71213D75CCE2BB86A000BBE2DB703C1C83513D2F3F27BB80B60DBE96E432BC628055BC702E183C605E84BCDA73503B86F869BD4B8AE23DB1CF1EBDED4D76BDE9FE1C3C3B00D5BC26649D3DDE2AFDBA7D47013C85EC123C4ED922BC5CEEBD3C6A834F3D90C0A73DCA22BDBCA4AEF23C29BB31BB565FA93DA3376CBC5A8A2E3DFFC5A23CCCFC073DACAF01BD366A90BC95326E3B5634903C6B7CCD3DECF9913B1AAF53BD7C7E06BB9C8488BD45AD49BDBF33D1BB2ED663BDEAEB1F3DA8480E3DBC79333CD9F3493D236C17BDB81BE7BB01CEEBBBA5AE9FBD55D9B13CCC3DC33CCE50EF3D7E7B26BC6D59293D93BD86BC90A4E93B0A8C14BD69EFCA3CD44B95BD376D5F3D0965F5BD67C7CDBCD6755B3C9887B13C85EE42BD153ECEBB11CA15BC4F31A0BCE2B0133CCCDFBD3A293B2EBD343FD23CBF246E3CAEC2BEBBB2A6AEBC1A8E61BD1353B1BA98396D3B1420ECBB73F5883B639F3BBC4A542DBC5D5A053D4329B23C70F6023B694EAE3C9B1CD5BC18E4C33BBBA0C03B7FC6F93BC87082BC902F983A74575B3CA07DD6BC1FA72FBDD9E7AEBBE8317D3B112AAB3A3722623C8771BF3C665D66BC0F74773DDA7348BD6FEC73BD5568ED3D5E1B3CBC09764BBDB440DF3BEBEC673D7F59B23DCDA68ABCE2C49F3B767A8F3C46621FBA03841D3BB2C2853DCCB78F3D68138CBCEA370C3DB4CA793C31A4B73D449E683DE1112B3D096AC53C45EAAE3C29A46D3C8ABDB7BC0E81D13C0E02973B771929BCB61683BCF4531EBC8493AD3B32E2E83B64435D3C8FBB9B3CD75A58BB0FA406BC9C5C98B7747FDBBC5C2ACE3BCD169CBCB9EEB4BC8D47A03CF51FA73B24CC06BDF56BEF3C738CB2BC37E225373D04F03BA01765BC9AFF163D33FAA9BBD3B8B63B46343FBC4E2A233B5E0DC83BE919FD3A6F113C3CD21902BC5BFFE9BC8E929F3D905A37BCF410CF3C642243BDF0907DBEE22BB4BCF0BA81BD1A02C33BF286D2BAC66633BD34F8B5BCA56E673D693C6D3C3A4C34BCE572173D128986BD9F1F8B3DFD80B4BB0244B2BC19DA50BB2AD291BD1BE03E3C211C533D2F12D2BC6B1383BC347FB43C88502C3D3C218E3CF1B30C3D04116EBCFC14323D433B23BEBED43CBC46D3F6BB2FAF75BCC0DC80BC76640ABBC7F4FE3B8C7019BEF205BDBBFB82A1BC5CE72F3CD8F28EBE4BC203BC2B3924BCF65BEDBC38FBF33B179F7ABB418EB03C785275BD5780533C8948C6B86BBAC83C190C42BD1A270F3D7A4BA03B54D58A3CED03CE3B3AC78C3B03F2033D4B70F73A17A5853BFBB1643BEB9BDEBBD2178DBDF340003C33EFFA3998D6ADBDB172BDBD2E7526BD5E7AFF3C6110BA3D717D59BDBBE1A4BD67DA14BD53824CBB67D703BC4F025B3CECC7653DFCF349BC09FD0EBD1074B1BBE070CCBD99F6EA3B4A80F6BD8F920ABC4C37F1BAAC73DDBC6842BBBDF541493DB8B324BD2F4EABBDDFA860BC61403BBD8DBCBFBCA5A8C139597B32BEA9EA523D46B3F2BC11F969BD798E33BD52661EBC703E083B6F73853CA5A847BD996E993A1C597EBCE07119BA9A9269BD983A96BCA1CCE73ADCFAEFBC79CA2FBD8CEFD93B854592BD75E2A7BCB9F6AB3DA6CDE5BB60474ABCDAC655BD37E228BC8E76533CF15768BD9F2664BE960D12BDB70E713C522100BB7B0C24BC6751FABCC27860BCB4DD18BD747B8B3DEB9A453D44EBF2BC1A4BD73C34D2BB3DB3C80CBAC4DD76BDD3A8F7BA1CCC4EBD4FDB5DBC64E5D53BE64F3FBD0A42433D9EEA1BBE9CFCB03B51A8243DF7ADAABCF11594BDBA04BB3C5D7359BB0A7B673D9B27A1BC61072B3CD1DC1ABEA118143E5EF9DEBD5C57C83B7491E6BC9DFB9BBB80105D3D8271F33ABE0F41BD6079CC3A30F343BB2AA7003DE22A40BC26A058BE399AB43C1C288E3D292E00BD71A98D3C598DF9BD2C4BBFBBE679A2BD12890DBCCD988DBAECA2353B9CC0263D6106CABD5D2CFABD2B9C37BD5CFF1A3DA563B3BC63C590BC05A9F03AD2E092BDBDF1A63D18FB49BDDE42853DB5692CBC448D243D9A8406BD821F3FBCABA6D53B3BC713BD2C606DBC721809BDDF8995BC986C9CBBCED1A43CE77421BDEF86723C33CAE3BC1C3811BDF44347BD5675083D67CC96BC563B09BC1D90E9BC257AF83A0C696D3C5AC1293D902510BD8005773CFADD0C3D12FCC93C3FA486BB52B9B1BCEFA98EBD6B1B0B3B449D883D2AC74B3D2A068F3D17639EBA05DE0DBDF106E2BB99FD9D3B4FE3A33CBAA18DBC481B3D3C29AAAFBC8F5F40BCFDFC193A1F4F653B6A6E4F3CC4A18EBC48BB5FBC5CB3863C2C7108BC1402E2BC4CF5513DC66E973CB5399FBA9977073C3EC890BC211082BA334D94BCBA34053C8490C9BC3801903B7B05503CDA899DBB413EBEBBED3AB9BC32397BBAE41B5A3C51B297BCFC7E623C599556BDBA2988BDD9BC173DC5608B3BB62E853DC1AC1939E35F04BEBAC5ECBAAD830BBDA3E1ABBA9741C7BB6B39843A3CE101BD67BC81BDAF0E163AEDB896BC488A76BC56F99CBD2AE99CBD558C853CF39FB7BD7F59D1BC4C1C94BBD03BE3BCDD5E2DBE8895C1BCD56DF73C0B021CBD592C0CBE24D9983C8CEE95BCE3D085B8B86E13BD04F8553B692C4B3C4BD53BBE8E18EE3C1EF7D93CF566B63BDC0CF83D0DC3D33B62DE0F3C427F4B3BE6FA4ABDE791C7BC4E9FB03DCB2BCC3D2D3AFCBDDC19BBBB1E05C1BCBB0C403CC416773C0685EF3C0E911CBA48C467BCC862283C9AD5163D509BAE3C561A303CFA6F8A3C353B55BCEFAF92BCDB82D2BBA03CBABBD9D531BC435E8D3CE85455BC0F550A3D489544BC5C3C9BBA155E6E3C78F525BD7477F8BC28A4BDBC06A723BD7AEAFF3CDE3F473BDDBDA9BC16FB713D45F1D03C9489283ADB44BB3CC65BADBC5ADD3C3CE7EE7DB9EB4F083D258E5F3CB71410BCA13B663A3E54D5BC53D3EFB935748F3C6107563A7D58D63C90B1373C16D54F3D67D9F1BD1CE36D3C3483C5BC965C09BD6B9106BE17B8FA3C82AA243D31B28B3C9C58AC3BB92D21BDDBEB9BBCD48F523CD7ACCB3C30478F3D85C0443BEB29383DD493A0BD5610B9BCC2ED4ABDC83AEF3CD3750CBE79AFAB3CD918C1BC2607B83C45F705BD607B013DDFBCF93C7938773C830E5A3DB38DB4BC1888E4BC76EEC4BA2A152FBD0ADB05BC774560BD2738A9BBEC4B55BDA9D5093B865861BC3798D93DFCEC32BD2CC761BD8D8E1DBD7C0391BABD3018BDCBF47EBBB444EA3AC6D934BDD065B0BCDE47233B816F74BCC794D4BCC2F74CBDB0E215BDBCA0EDBC180D943C20EE2CBAE177533CB793A63C661F433CDF4A1D3BAF48FE3C6472413D4BBD27BDCD1F9BBD2BEE2C3B9C5E56BD54ECFE3B82FFF13DE0A8FD3B58DDBCBC06D6033D628C933DDE52063D0979DDBCF0EF92BD33A3EF3B335663BDD95ADDBBB58F923B832B473C763C58BCE57B803CCF318ABD33A9A93D8F5BF9BBA8D57AB9C2546CBD26A4C93CF3D0F7BC621AC33D540FE13B1908A93B683D603ADFE7CCBB2C42CD3AEBBD48BD324D18BD3D340B3DEAC2A7BCA295FDBB4FB1043B7322D8BD01464B3CD050A7BBE3FAD6BC31CB063DC099533DF17D00BD588872BBDA41973CC7AA05BD950B83BC6B810FBEE8F680BCC41DFD3C65A44EBD8ED122BD4AE5A7BC796437BDCDC609BEB91F733C5E89823BD911A2BC7BA1D1BA7318EDBCDA26A7BC5B47433C3254073DAC270EBE4E2FB03CE96BF53C32341DBD9AB338BD78237CBDDF9C44BB1A6055BDE99906BEE038453DC358A13C197103BDD2B88A3C43B86A3C939201BDDFADE5BD997D5BBDE9E011BE57A5033CB5B11FBE7D9CC1BC8BBAE8BC1F519C3C986ABCBBCD980A3D561D57BD9BFFDEBDCA96AF3CDCBFEDBB0AF4CDBCB83AA8BB022205BC057D0A3C0ACD00BD4E1DD2BCFBA48CBC5C44923C00550CBC048871BCB7C297BA5ACEB13C6F0020BB63E628BC0DD143BC6C9D9DBC6386793C3E985FBA8D575ABA6D8170BCF669C2BB80EF5C3DF4A88ABCDD9E913C2B28F1BB56BDA03CF3D8E43BD632003CC6905C3CF6E9FD3CB247CB3B6CA9FBBD32BA833C63CEBBB91CA3D33DCFFB673B0DBC8BBDB3D616BB31AD4A3D8172D73C9783ADBD489D173CF726FFBC1290CD3B573E903C9EA8123C40E8E23C94E794BDA3DE3E3DBC7253BC80BCAB3CC5B787BDA2009F3CF16BE3BCD4DA9C3D6D9A2DBC9020E13AA44505BDA7F5283EA1A170BD8694A2BDA24C9B3C94FBE8BA95BE91BDEBDCA6BC9C0BE2BC19A6943CF3260EBE1027CC3CF5E9513D2D2314BE97240C3CACBE193D3B12BB3BDBAAF5BCB8E44DBEFE1FCE3BE5A412BC0896AEBD3A132B3C7ED1B43C980942BCDB581BBEDB054EBC8E3FA8BDB4F92DBD289D14BE2968E0BCD59F5ABDE99D833A8D4CA93DE74914BD285D31BD345C94BBC11DD03ADBBC95BD6A7DD13C8C6E43BC09992C3D17CDA73D585913BD0F37633B5ABC953CE96D883A9521DDBDB19F5DBDE52E8BBDA083563C0E59113CA68408BE07DC0D3C89CB2EBD62AB3BBC499F2DBDDC572F3D5A10EE3C687BD43C870A693CCD63943D8732C1BDAA6C8EBC215C9B3CFC068C3DE3D5C4BD4FD9E4BD93B4AE3B2284F8BD2D63EF3C8B4F92BB40B79CBC903A30BC08DC03BB226E1BBC101531BE13EB4FBCB9A0DA3B538A8DBB012344BD1CF5C33C67AD4A3C8B8EB23BD41042BB3CF83E3C78002F3D5C83F4BC575A36BD52806A3B2FC557BB6B60A83AEF3B433C5E97CC3C3EFB5BBB351561BB2700EE3AF8F1563B11FE0C3B720627BCF36FAD3CD24C5B3B42CEB63D38D93DBCE6D720BD3E24A7BDD319A4BC2EB747BC6ECB513C9AA2183EF06B033D419E16BD66F9D73CD1EA173D9E4C23BBDD26D3BCF76DB6BD080A3C3D5E3465BD247A42BB684D923D3163B9BB7E3C07BE600A08BC9C7D3B3DEB6CA63C8BB912BDC082053B1D9D4EBD36C3923D211319BDEBF50D3E117788BB42E805BE13CEA6BCADD202BCF89DB0BC9F849D3B0F2DC1BA2E4D913C774583BDDEB5FD3BDE7EFFBC858C4C3CE58FDA3C28938DBC4E2996BC7475B9BC53E24A3CE558383CDCC634BD67AA553CA9FAD03D67650BBB053A633CF01F3FBD2A4289BBA3E9D93B7070613DA910423C55B135BA22B1BBBA059E8DBC1A8F233B6C7A683C484F87BC4770C7BDAE29873C2D1701BEF5AF793B6975B7BC9926653DF495AB3C780A52BD9E0F643D0CA17D3D5C8B593B8E18A7BC4E1D4ABE0671753CF9CAEFBC3AB077BD31D30C3BC23052BAD4374F3CFA82FBBDA0EB133D4C43B9BD1E7196BCD98432BE9442D0BC32F686BC0B3B013DD09603BD3A8BA8BDFB8C1BBD7A5D853CDF05F23B488EAC3DE8D23BBCF5CD1CBD95008CBD4DB96C3C9F9DA0BCB2F7B73B9F4F083E0044443D807E0FBD7A6101BD9538C43C954992B8CB404EBC003F8FBDF78F043D17228BBD13F9E4BCDC945D3D45060B3A806CD2BDD089B0BC9BDF103D457A3A3DD66F7CBADB810CBC2EF444BD86684B3D7516FFBC2363F03D1974A7BCA8D2E6BCF3BB34BDF3BF99BB3A0D553B361A143B76FDBD3C43A8C5BCEF8E38BCDEB0613D7F5B5F3C515232BCF1F6963C5F557E3C054D42BC4A75D2BC4DA69C3CA5C404BA833FB03B5303E5B912FDAC3BF3998BBC612C723C9A1B1D3B0D3DD9BB28C40ABCB3CE2B3B16EC4ABC075BBC3CD9B245BCD26DC63A3F7015BC8E82FDBAA7A022BDD0A78BBCF73736BBB39626BC8B9E9E3B3A8E843B4FA709BCDDC810BC7350DD3CC49392BB1F0A1A3C67D7DCBB8E4D953B602912BB821981BCE7B67F3B2F528BBADFB79BBB7AAD1A3DDE86F33CB4B763B939125E3C464F57BC81B7CC3C53314FBB5DB57C3CB6BD853BDAD804BCFEE4013CE30589BCC25884BCC9CD503C0F25B23B75C576BCC914FA3C612535BD6B2654BCC36EBD3D75199E3C7F445DBC78260EBEC556CC3C111B113EC93F083D027EA43D571D67BD1C27C9BCCB8414BDA49277BDEC7ED73CDBE1A93CEB73AFBCC2E949BD1D7BF7B9072E2DBC982A1CBD18087FBC6BD2A7BA1B55C63CF7EADEBCB6FE13BDC5A0BABC83CA1D3DFD8025BD29CE0FBEF728283D3BABBA3BBE551BBC44681CBD8D373CBD644A843D5951FCBDECB4D9BD7199D23CA26BAD3D9556233D9D9A413C044CACBC9363DCBBC179F6BC1B70723BC8C4813C20987D3D859252BD1B260EBCD21FB53CEE3A2C393F94883DC587723B8D0C603B86B2B73CD8E9903C4003BBBCC676803A64428B3D332D873C765D34BA2E87AABA3072BDBB5959F6BA0A7D3ABB720B213CB93795BB13731EBCB175C1BB4023123C7581B6BAD283363CF81876BB9B1B4C3B60A887BB5B9E25B92598FA3B7BFB3D3B2848123CFB1C1EBADE838E3B546784BBD54929BCE254B33C16BA58BC7AF14F3C00FF26BCEAB0CF3A26F7003CDB87C4BBF77B6DBBA2B24C3B61E14D3BEF7D52BC9C2BA0BD805F7C3A1E0EBCBDBBBBFABDE4CEC7BB24C3B4B9C7F1A83D45C7AFBB7D7FEBBD1DC086BCB7D95CBDCB9F73BCF5CE823C99E27F3DB19B5FBCDB9B13BD68F9723CB57E59BDB0E450BA10A69CBDB67E90BDCCA2173DCD3E26BDECCD9CBC2A23F13C806F3FBD9BE81BBE9B86023C8CD6A83BA939523DFA4EA53CC7D15BBEDAC8A3B968BD80BD243353BC3AF6763DE7C8C03B3837D4BC4E09973B412555BB111D99BB9A633C3A95B558BC401FAEBC1DCA4E3DCCF6A53B1D70173C86C467BDF4791F3CC1BD953DE696AD3CCCF75A3CA1DF0F3D798F96BBBF221CBC5B386CBCB7364EBB544FE93C59879DBD1B528FBD3A603A3CED7BE33C1690AE3CC22DB2BCFE401C3DAFA4A4BC7642F8BCB7FD223DA1D522BCBA5968BBE846C73D1EE8D0BC6AF67BBD80DFB1BC0E93A93BAC558B3C14A628BD0A5B32BD7E722DBD20FF95BDA1BE473C3EDA24BD3317123CD5E7193D4B3CC33C37E6B8BC3684D53C6DB71139F9EE6ABDA89993BDB3BF20BDA43603BDF7354A3D18658CBC2F9B07BB50FD44BD6EEFDD3A0F8ECD3CB480B93CD061EABDA051523BCE96303DE04ECFBD0D6156BD3EF389BDB72B24BD673D1ABD198BF0BDEC54A63CFC2F443C572F10BD854F38BC647B29BBF11A28BEE707F9BD4D19E8BDF7A7A6BCC274E13915AEDCBD2E2FC9BD7CA26FBD99F3FB3B0D4A6C3D2E881ABC1BBA07BDA69A90BC8332A8BC365D063DD6FD1B3D1621FD3C1009C5BD817F2F3DDCBD8CBDF252C2BD1FE43C3D7DC933BC3A5CB1BDF42B13BD7462823D223DD13CC8F28BBC46B5773D81749C3DCE8D0B3D79FF1CBC61E49CBD55504B3C2A25B5BD28472A3DBC8204BD1C0416BDB8AF883D3C2954BD19941E3DD407ADBC3423E33CB1C589BCC53D373D90008D3C47C699BCFEC5DB3B5154D1BC76773B3B3856403CB0D658BDEEA4C6BC47A84EBB16CFB23BEF5222BCBFD0E73DA6E4DC3BB438993B9A333D3B8B8EBEBB221DC6BC5F9A383D00ACEA3C0339F5BC65F78B3C809BA73C079C6A3BE771D0BA75433B3C5495F4BAE0AE92BC1669413BF69E723B76FD5EBB514191BC3E5A333CBF808F3C4774E0BC9DC3C0BC12DCD2BB118F6CBB46A941BA602A053CF50A653C22FBCD3C23473CBC670814BD54BD1CBD40E2BEBAABC2F3BC4897CEBC1A4C913B960E2CBD41738BBC3B88DC3C01D31BBCBCD732BB4742143C3AF43ABDB6E6B03C49ED5B3BA8C0C13A3E634ABC8F1DCFBC485544BDAE8EA3BCF71A643DE01B2BBC0D3E47BCAB18F8BD9E2A93BCA9D102BD3FDB7F3D2FFC21B9F365683CDD6B303CA991963DB796BD3C22C510BDECC189BB666700BC3C927D3D43B45ABC2EE60C3D9C82F33B223969BE576304BBEF54DA3ACC12E93C7685F8BC0F1F86BBA9FBFF3C618A663D280844BCEB48533D70ADE0BCC5B0E23D9F947BBD52A068BD35DB08BD8E83383DE172AFBB236DB6BC27AFA5BB873D3FBC6276073CFE07B3BB69FA1E3B9942A6BC4CBB96BC556B813CB1C6C93C7216AD3B519ED03A89B812BBE3F1EEBC5EB30EBA2464973B85B358BD28364EBC05CB403CD030C2BB4B94D9BCE16FE9BBB289ADBA3D40503B5F30BCBCA655383D9EFFA4BCE93EFDBCC49FAA3CDDA6A1BC1ED053BBEC1D88BD71F97A3C0CE0773BAEE605397F1476BDA9AC60BB299E0E3DF8E30DBD8B46143D498BE23D795F89BD3541C8BC6DBDA9BD44F8D6BC34B402BE6418B4BD902030BDB31AACBCA54B17BE9EF2D5BD0707893D9BB010BD53B2383D817E4CBEDCEFB4BD7B48D9BD9E71563CAE81ABBD42FBAABD99F783BD86CB123CFEE1833DAF600A3CD24DE0BA8A49EFBC403C69BCB919623BFDBA4CBBDBE4EEBD0C35293CF7BB7CBC3F96E43B03AA323DCF09323CB40D32BC765071BB7D47093D56414C3C4BADEFBC9091483D82C2B93DAE11F3BB8304CA3C2130AFBBB928973CAB4B83B98207DA3CD7B8B23B003990BBA1A301BC6D32E7BB909C363C59D8803C5E9F05BD3EAFFBBBCF478FBB35060BBC51D745BBE1CC09BBDB58CDBC0900BEBB1629ADBC787A4EBC8D007EBA9C3B4CBC3028E8BB982FE8BAEAA6623C226537BC4109ECBB5006FCBB65552DBD4C328B3B3E75163B561F64BB9F4F8CBBC17649BC3E34C8BBAD77B2BBB41DABBB827C99BC92EA833C4A99B7BC300686BCA6E0883AD93723BBE28814BD4B4C963B71D707BDBB3FD03DF4344CBD3B378B3C2E880F3C0D3B17BE147AB7BC02AB023D5111E5BAD24961BCE3C31ABE3B36C13B70406CBD4F9837BDBDFC5CBBFD5CFA3CB95792BD6BD610BEEEE2AEBDD4074DBD0BA08DBC827CC0BD8B666BBDB104DFBC6AE75DBDF3F20B3E577A75BD7A4416BD83FB7D39C0BB15BDE812B13DF50CB7BC9F2684BDE1EC51BD44EA94BBA22B193DEA4D0DBCEE5068BE191D9C3DC6DE953D4208803CCDEB453D5780A1BD5F55B23BF01BA1BDE2FF383C414621BC61D52CBD3465953CB44594BD9074CEBD90C30EBDE06A3E3DD0B5C2BC3AC68ABB7E109FBCAAAC17BD5ED0263D7E9CB6BC40618E3DE83AFABBD6A0883D10BE96BACE13AB3BA011173BB12BC83B772F8ABBA35CC8BC06BE6FBB8362423D46ADC33C0CCEBD3BC2AD553DED85573C8A0D6E3CF4CEDFBB14DF1FBC87FFAC3B2F4949BBAF6620BD0FD11E3AD34CF5BB3E85633C1D4ABFBC76B2FFBB8CFA5CBCD4A619BD7CC67ABC438F8DBCF32DC5BC4F6E0CBDFF21FF3C395BCCBB0BCBEA3C5EDA923DA562AEBB0EAF40BD74987ABD946549BD1E96B4BC4F0D713C2EA63DBD43D8F63C252D19BD3D77833D16D7DBBB7FA002BDDD05A0BBD56808BDEBCDC9BB3A2A8F3C94AD3EBC002ECB3CC34C49BCDD19C8BDCC249ABB94DE033D8DE04B3CD97113BDC71144BC7A09213C61D34FBD4AB24D3CD3CB443D4DF3E93C59D237BDAFC35DBB5F84913B3F3EA03C408EC63BF111CC3B33D374BC24CF01BB395E52BCF899B43BBECF003C033E27BC64049E3C73AB6B3CDD68AE3AA6D671BC447C45BB46FED0BBED2C5ABD6C29CCBC8745E43BE04B17BCAA6CB13A89D69F3C752A3CBB092393BCAF0D7EBC69282ABD38C4443CE26591BCD6D25EBCE8F9E5BBA2F7CFBDE4F6793C316D18BB9F25D53B26ADB9BB7251123CE67DFBBB9F5786BDA67EC93CB1EEAE3C7DE130BCA1F4ECBD51C3EE3BF6AE393C41A11F3D1FBDDABC07152ABC7558F03C8C01653D0C6A0FBD975D853967DEDCBB2C9A41BD800DB03C90DB883C37FB82BC17F030BB30FA58BCE0B72BBA160E543B288518BD9BF2C53B035498BC997C8CBD8A8D92BCCB63263A0E6D0DBDD4171A3DE8069F3D533D26BD361C233CCC41A33D3BBBB83C980A2C3BF7036A3C3C9C08BDA6345EBCFF4D6EBD3A6A243D8C15233DDF9C0F3C847AF53C9E3552BC4ACD323DD4E933BC3DA9503CC65FA4BD0B3901BC9A67D8BC74A4963D61B854BDEABF4EBC36C40BBDD628153C8737C43DDF3D143DCF35473C02F352BCDA0387BD7991933A3F16AA3C7D862FBD933060BE5BE9FCBC7E4E093EE3BB5ABD79B647BDB05FC33B8DA3E8BC9C66273D04EB113D4984CFBBC9B8C73D934ABF3C16DB72BC5B03873D9D5612BD506B2ABD2177C33BCC3EEDBB7251DE387C390EBCCA0147BD946F233C5573393E57FA2C3D29B03ABC13B6DF3BC4CDA93CDE903E3BAFBA1F3CEAC28C3C020BF0BDE05DBDBAD868713D1D477B3DD6DF02BDFF2005BD6FEB11BC44450B3BFD37E8BC5E128CBD1ED4ADBDBA0A0FBC4CECF0BA8206143C2BF7B13CED4A3D3D1C522C3D3CCC333B91EDDBBC72E6543D66EECFBDD582293D24F361BD914B14BD033A58BDCBFC0F3C0DB1863C596482BD803CD33C15DB2FBD9CBEDABCA394E8BA242E033CCBE703BC0BBC22BDB2F9C7BC6C9C9A3DABC58D3CA68D453DADA496BD422352BDB497CEBD04DA51BD6419213C5E2788BBF49993BD2878F4BC1A05533D35B01ABDDE4AACBC71D90CBDFD2933BD08B7743CC97516BDF7051BBEA42CCBBC5C0EBB3C28B52DBBBC8FF43D3B75973CE22C23B80B6C7ABC23329EBCC3CDBB3B701802BCEBF3C6BD715D4C3DFC5DC53BD17155BBF82458BE965892BBEE18AB3C971CEEBBD42CC9BC4F062DB99BB0FE3A103BABBEBF3A54BC1C20163BB3D639BBBB4108BDDE9D90BB76DEA1BB72C0293C053375BCDE1A3C3BCD5F203C47E5ACB9E0C7E5BC82BE823CC95423BA37A9B6BD063DB9BBDAE0EDBDB74FBB3C2367823D555F8B3DA448263D578D6A3D6E3AA8BA1AF6B7BD7506ABBC050CA8BC8E8C00BD1ABBCFBC39F21CBD4C11CDBCD66ED7BD9B74B8BC2E6B85BD0708083CA5A36D3D88A4C93CB3F9753B19EBAC3C94D60ABBA4ED10BCACC0D2BC5765BBBD2422ADBD9A10A93BEE46F53B3AECA5BCA22FB5BCC388C7BB5D80353CB2AF2DBC94811E3C0EAE29BD1A9194BDFE1CD53D40E010BDC78A6FBD204CFD3D0508173C3507C73CDE32D23AC86E2CBDF33808BD21A4C13C1272873D0AB3FB3B17170ABC561DADBC23061E3D0726F6BC0954FDBCB63C0BBD687B053B10DE2D3DFE221F3AFBD5403CD2F48FBC06846EBD33AB95BC494D82BDD56935BD0C13783CCCEF93BB5A4AD63BC50985B9FE42083CC63CA8BD9855343D564FAC3CD35AC33C4762C93B78A6293C72257EBD645285BE3F66E4BA8156DDBC19B816BD90931BBD8A4F2A3D3B2C2D3C08FD2EBD816FA03CDBE44B3B0B49B43BB3146FBD5E84E03CA86D11BEA8EE0B3CF92459BD38C91B3D5698D3BCE7FD05BD536E843C3182A5BC256C60BC0F18793B7058A8BBE0C58B3C7339DA3C67EE1CBCD05BD23CFFE13CBCEE3088BE61EC313D547E9F3DFF1436BCE47049BEFF113B3CE17DD93D4EE5A53B74E9483AB870B8BA576124BC6F61713C61CD9F3D5F0C0C3D9891323DBC2140BC501011BCF27A44BCA435E13B36133A3B0AD285BCBEB2FBBBC6A314BDB433313B3342C2BD3EBE6BBCA5D6BABCF938E13C43631FBB478E69BCDA12C5BDE22C803CE27E30BE6506A83A6D67EDBD33ECACBCBD58E53C22C58EBC4D9B3CBCCBABFA3BB5AD1C3A4E84E33CC66E8ABC9B92213CB496163DA91C34BD48749FBD596C1ABC79AC8F3C26F010BD68D4B53B18457EBADDEF9ABCA2A6C5BCA3B514BCE03C9CBBA8ED0EBC8D629E3BC8587ABCEED2533CF27240BD8C82813C73F280BC1B2F69BCFFDAFDBC26FF60BD4F863DBB68BDABBCF649B2BC76835CBCD436313C73CD3CBBC936463DBEEA2FBB8854E9BC2F32B6BB5B6CBABD5586043D16D69F3D73E57A3CFEDAD63B8C74943B06A086BCB0499C3CC9FD6C3CD54CFBBD552A7B3DAA8F87BD4725A23BDCA682BC08E747BD264ECD3C7F8C17BE22A8BEBD146EB83C0923D8BBE774B73AA3CB743CB47C5F3D8D9C8FBDAE939B3C97CC903CC59411BC442A00BEF010413D0D19F53CA44B1A3DF60DAEBCCBC9C0BC5F5B80B920F75A3B78C3823C302B65BD02F5B53C0669163DE18464BDD8CB95BD03938D3D7BB91ABD4625973D926B87BC1F002A3B0D5EB13B3B132F3C59DF023E502D28BEE0E1DB3B83CCE5BCAC8EDB3CD68B2ABC2EB2FE3B06790EBDE91C83BB62870A3D2FE441BDE2375A3EE927C5BDE89A803C663C34BDED006D3DB2B63B3D6EDA173D667C7CBD96B6ECBBB0701EBEC2796A3CF83A2CBE9B32AB3BBEE715BCCBED763C77AC593C7D3C7DBC314EC03DC912A5BBE1AF07BDBF9587BD632A56BCB0A9CF3B8949083DC448913CA0B991BD2B8384BC2EAF1EBD16AA193C4914D83DA587C4BC6545D8BAF0CB243D91BFBA3C0B66EFBC39E22DBCE0ED743CE4822BBDD1623CBCF0278B3C173FCEBC6F491CBCFF8F513B514516BD25BB2C3C571FA33D3E0F083DD0DF493DEB1C143DCBCE4DBA329439BCC139233D0C34E23B75E904BE5ECC19BDECD6A33A41353E3CC91ED7BB444E0F3DA9AC2D3DB89FF7BB232B29BCBDA819BD8198E9BCC5B3AB3D69A852BDFF72433D0E985B3C6D6B40BEDF8C05BD1A097C3B2408263CCC3BB9BCB4AF99BDE63315BBC8F8FE3CA10998BDEFAF00BC53FD0DBD5F49913BDA7192BAE398993CC07829BC87AEFFBB6CA62BBBECD72D3AD2C9C03B5FF206BB9E440CBB58960FBD4E742FBCA49745BD1F20C8BB04D5BE3C4DC02ABBB05A1E3C4F6D153D24CE5A3CC6DEDBBCCB452EBCF179EB3BC62BA3BC8A73343B26F2003D9F9B1DBC69395D3B3D96443EA07810BCECB19ABC7AB821BC7CB5D93BA251933C75C22EBABFF7673C96A040BB55CCE2BD4379EB3A384466BDEE3B513D4FB6A13CA576033D23722A3D8ACD023C3FCFF53D8DF249BEA6268FBB2BA060BDDAD23B3D3D7ADB3CB1000F3CB2FCA93BED933FBD31EFD43DD1401DBBF331323D38BB943D93FF523DD7571CBED0EB1ABD6F7C1FBE6117253DA0CDFABCE03300BD513C61BC64F0583B3EC1103CE9FBBBBB723E103C0FF735BCC5F125BC93790CBBD8D87EBDAF61A7BB4C387CBE3E36BC3CE424943C4108C8BC8B2C8DBD8AB0933BC6FC52BE793F0CBBA46F2B3DEDEA78BC64499DBC185662BA5764383D059BDE3C5657153D07BD58BC60209DBB2888923CD56BA63B09D0303B528AC83CCDB7F23C53EF3BBCBF01213D276E383C377B9ABCE24105BDAF3B12BC9B91513E6C1ADCBC462D4E3B08FEBC3CC8D4D03CD587343CB630563CE434DB3CAC5D89BC712C6F3DA9B08E3D0C3154BC0A77B4BCDD6847BD6F9DA2BBE012F8BDD625513CE6D78C3C5B8DB53DF10CF3BC0B4E55BD6B45803C48AEBFBCB18CF1BC9BEBBCBDA1AD66BDEB685ABD796806BE05BB72BCAB1EE13B31110ABB7F7FE6BBDE47C73CDFC5A1BC61DD6C3C50F5C2BDFB73B93A753874BBDAB276BCF73CFD3CD34A493CADD50E3DE29672BC80BB5FBDC894903D514C493D35A888BA94796B3B7A457EBC71651EBE568DCC3CBE3825BD859E963CD3393FBC71B3C8BCA6FB9CBDD53B1BBD72F78ABCFC3A913C1175F4BC4C110BBC27F44AB891A3143B78CBD53C7C2F10BD60076D3CCAA2D93CDE3F43BD3F3B433CAA88BBBC7ECF26BD8BA4A03C27D1ED3B954A53BD914F76BAED33D3BC46E4B23C6D6525BCB67A443D3752C2BDF55B363D9DFF09BE314D8E3B8E2ECABC403A1EBD9023FB3A50FE403D868A9ABD738912BDA3953FBB8C6C66BD4EDCB5BB9E8FF43BE724AA3C9E6BD23C86996DBC321711BE8DE54B3D1B43873C2EA7BDBD05C285B96FE5D6BCF1618FBBB275933C46030FBB0406CEBC7CF57D3CCEF062BC90FF753C3AF59B3BA530FEBC3EEE46BDE4CC1B3DBD7DFCBD4D22113C66A816BCB4A3E4BC4FDC623BD9865A3C29F3C9BC8164D2BC146D95BD22AB92BC675071BC55EB3C3A7E5A5ABCB8D9B83C01792E3D9277EBBCC091FEBB4FB191BC0D8A9B3AECBC9DBCE0B8ADBC2A3942BD0C1951BD7DC5B1BB62D11A3C83868F3D95329E3D135DF2BC4D0353BD5DDB5ABDBB1907BC65B99BBDAE3F9A3C8CA2093DE125B7BCC867CCBCAFEFEF3DBEF98CBB4C3F64BBB194A3BB572409BD0464AC3CA313B1BA65C95FBCB1A43FBCEB77FEBCC533353DD29A1E3C3E4EB63C76EBF83B5ADFFF39D60FDABCCCCD3FBC320EEABCE42AA6BCFDC1D33C5AEE8ABB547A9A3DFD4C073DA8824C3D650FC0BC44EA6DBCBF6E0F3CF52E09BD5C7E03BB13E3053DC85D93BCF6E1943CC7D7333EE58380BCB983CFBC7440563CEBC02F3CAF6924BD9022023DFA5277BC9D836F3A633E00BD5DEDD9BA1836F63D79A409BD1515933CB825E13C5B9ACBBA23A32EBC2E10FFBCF3AB963B27A0CD3B1084673D05CDA2BB7DA7B3BB128995BCF5C8633BEF0CA9BC8E93C5BDA38FEF3B6A585A3D0C29683D4A7EA13BE1E7D3BCBA6D9E3CAA25123C1AE4193B0044E53BC62020BDF71F01BC4E96DEBC1D1EB9BD9E22B1BB6C5573BC2B633ABD088D2E3C65820D3BF0E5FD3C6F24143C945360BE93814D3CDAEB393EF0A1B73AB4DBECBD32041CBC6116CDBDAD6E373CA276213C588F5BBB50DFA3BC4F701E3C6355DB3DF112193D5F2D3C3DFEDDAE3B921854BCAF47CFBCFB19D8BC63ED373B2A957CBC93CC92BC25FB72BC5AD844364F8EC63B86B6DA3BAA990D3C7BDA2EBB393DACBC60FB793C2C77353B3BFF72BB876B5EBC003A36BD7B718BBDB96AB8BCFAF8473D8CE47DBCE0C6DB3CD076D03C8AE423BCAAD348BC22639CBCF460FB3ACCDECFBC7E3A35BAFB60B43B060450BCA5DA113C5FCC913CC58A51BC49472BBCFB5C0D3D087295BD1503B8BD44AA8FBD5364553A84DE18BB5820BA395DDCA1BC5FE0923C5EDB1C3DD0EA853C5698A33CE615FA3CA5B34DBB7C16EB3CD2F320BC97238BBB98E9E43BCEC969BC7786D2BDA438E13D0A7EABBC60079F3C18DDF9BDD446893C471BDE3BFA839EBCACB1443C7BAD8B3C82B1803CC787533CD7B8BCBA019359BC9F165EBD4BA3DCBC002C23BD30C99F3C379011BB067D67BD465DA0BC687F013DDAA5D3BBBC0BB43D13A8AA3CA84143BD560F02BCC8961F3D1051403B696FB03B8D9BB9BC4995AABD578C4B3DFD63923D0DF853BCC0EA7CBB6B28983C8CFE313D116A97BB7F779C3DB26E0B3CF0B044BC90EEEC3C133734BD5AB0D53B28DCC7BB3C3461BD8037603CA0E900BD67A0D1BCDC2D803CB6F1123D19D10EBCFF3D63BE8227CE3C483168BCC3FC12BD66B4A03CE290143C1230C73C793312BDE6A54FB8328518BDAB0F53BDE9AA98BAAF6C153D622A2FBDFA53D9BC9460513B37F790BCE5D90DBEE5D4D63CC2191FBC0C2726BC853900BCD6FDB0BB97DB92BCBC2B5CBCD9619F3C17064CBD4A8FF83B454F9B3B3B79CF3AAC0C0ABD7430C7BD0EF0BA3C8DCCEDBCFD88503CEE8E083D7A2D93BBE218913B9BA3863BE4CECABC6000FFBB6ECA593DB9DB89BDC728713A7DEEE2BC474D06BDD1C532BD91DFF5BDA3A3CC3CB7E9A63DB4A0B93D277B2C3B7E63BC3B80B6BF3C62114D3C22777E3CABD79ABC209726BDDA870CBEABA08D3C478ADB3B7763383C8A6C04BD7BC0CCBC8F683ABE7E9715BB12D3FF3CDAC5673C1FC99CBC5E6686BC2459A9BD580580BD8630473CBE39993DB801C9BC0E86353BA617DA3C7C1A243A25DA6C3C19C7B5BDD1FF9FBBC942343DC05909BC1FC78E3DB77450BD670760BCA8BB3DBCD6B6463D28F390BCA8A934BC7810C1BB364CB6B97BEE5B3CF34CCEBB6066B8BC32E7803C1365A5BC438D95BB1C1A09BCCA27E5BB1FBBB4BCEB2745BD421711BC0FE64EBC7BD7853B445C28BCDD114F3A12A524BCFA78063DCBC960BBE460903A542DB13C99A07A3C0D3DBB3C0A84333DF176793CD4E3B13C57D1C6BC7294FDBCFE3A2DBC28053D3C11828ABCB845903C48F61BBB1CD090BC1CB7683B04E288BD97F339BD255F8FBD8E2846BD70EB043D5642383CEB5236BB9EF73F3C1C80C2BC65D413BCA4C51DBB80FC39BDEAA75EBD3B95463CB1BC4CBDF37A9ABD117E2B3D8B1EE5BD2AA5AC3D0EC439BDC284933D0C0EADBC9919AABD6CC0AFBCB7F4B73CCA3DBEBD910E46BC428DF0B93668DCBD31DE46BC8A79273C90646DBC6231F3BCC5B2D3BC77B561BDBFEB6CBCC2BB003D03C249BC148B62BC381D66BDA91CB0BB269C85BC5C34233C05377D3CB52582BCE94C1D3C65D68ABD9FBE00BC195C45BCE49DAE3C259915BBDD611CBD67D8073DE042173D68F2A1BD791DCD3C1655DEBC9E88E33CE62F153D737242BD1A6323BC5EF134BD78AC06BC630DBBBCAD2FA4BD887F553DA73A60BE4F8DE4BDB25B8ABCECF4A13C54B2A2BA337944BC2D9156BC1EDF113DB7611DBC5D53063BDF51B93D655C7D3CD36A323CBCB9D6BC285C433D6E1E20BD9F88593B887F29BD52282F3DFD9DC03CD16422BDB690A83C5191323D8AACE4BCF32ECABBEE58ABBC3434F43A2FC446BC075DFDBCA728F93B3AF8763CDE3F22BD10A6BA3C99606DBC59DB3BBCE8A2C3BB484B7CBEC26D363DB17B973DCCB1BBBB28CF293E802DE13A5BA6B53D3510493C20E2DF3CC6F495BC2EB13DBCC169A2BA3B6DAD3DAFBF1C3D35E4E03C5FD3BFBA253196BC5747CDBA07888EBC46CB90BB4230CDBCDA6F4E3B1D24183DA69AA5BB99EB99BC6074AD3C68CEF2B8997A09BD5CD82F3E772C4B3D2CB43BBCF7A048BD0F2F6CBB7A0482BC2CB41C3D812E7D3CD3A1383B2C798FBD03251ABD6FD2C33B331C2B3DAED948BD3B8CA0BC64FB73BD20CB6B3CB7755ABDD441833D7C44A1BCE6BCE8BCF5EEFF3B6E66B2BCD02274BDEF5190BC8A6C44BCD62C2BBC6F3D22BBCBF28EBDEF7E53BB5062E03CF9476F3CFFED0ABBDD26A8BB66A749BD41C432BC6D7B2FBEA3B9B6BC9EB32FBE155BCCBC310E1D3D276F1B3CE781C33C868FEA3A78F2973CAB2F933AABFB74BCF6C23FBBF8E3F2BBD6DFC0BCD787B1BC805ED1BB3513AF3B11DA633C6BBAF0BB225B84BCD8CC6CBD148EDCBC26F0E3BD29597CBD33F6AEBB3F8B903BB39D97BC0C4957BC10127C3E4B2E9CBC5618BD3CF3D412BE88E94EBBFCA445BDA68D52BDFF50E23CA32AE63B696E2BBC124CB0BC5D039D3CB32AB9BC3FDE04BC4FB60EBD9B913B3DC6DF9EBC57C8B3BD186C39BD3B23FCBC1048C9BC77C78BBC56BB6CBD129A96BDBBDDA2BC8A91D93C49FF953CB4212FBC7374E0BC0E446ABBD7CC1ABB465ABD3B19C52A3E961F713D0E556CBC914853BDA57F9ABAE93FB7BC00F41E3D26E15C3C14F25DBC4CDB76BD07D86D3D587B693CEC0C4D3C679A5BBD9056F9BB50897CBD7439CBBC87E4F8BB15BBB13DBE5D40BCDF7FA7BC4E96503CA5372A3B1F4D4CBD3630353B1C7777BB7F96A7BB8244E33A0256E63B1175DEBB133A54BB3E56C93B0BF8ABBCB9BED6BBFDAA91BC7820193BAF36B2BCE7CA10BDD4B527BCBF6691BC02A70EBCC00F203CA85FF23C61D256BC35A29B3CEF5C9EBB7A289DBC64E45D3BD815F5BDC330623CF3A71B3D66E896BAAD99473C77B1183BEDFF0DBC1D38673C874B38BBD53E69BC44FC983BE014E3BB48089CBCE725413B897789B90C85DFBB1A5FFEBC1CBD573B6518A3BBF043B6BB31BAFABBE0146ABC6F628BBC7C13ABBCDCDBE83C2EFB1FBCC94C453CD4122E3C93A1C2BA919C853871BFFD3A3C391639FA520ABC927007BB4DE7A43C9F84F6BB8EA73B3B1A6EC03CE1ACA3BC9F2E07BC61613EBCBEC2C03B2A85C6BC3BD381BCB91680BC78D904BB73311BBBB77F2CBDAF0D7C3D43E7BD3CCBDC94BCF8DC63BD0B07453CEB9E573C142A253DA6A8643C811B13BCD27998BB517675BDCB30CE3CDAD0C6BC27AF06BCE3AB85BB8B3C223CC1324C3CD1DF35BEA112AE3C5F7878BC99F0ACBC34E7713C7C88143D8072263B3BCDFEBC230927BD7E02CCBC77715A3C929E17BE129157BC8099613D523A79BCDE94F4BC4BEB3F3CFBB79BBD7E2F6EBD97790ABDD7B6D43C7575093DF368A2BD90D502BD8512E6BC09CC7E3DE7D5763C36C0C4BB47CC24BC79E45DBDF7EEDBBC4BFB813B02697B3D98CFD03CBFBBA03BE2C36EBD7C21DA3C0113CC3C5454FBBA1912F53A68D750BC26360F3CC4FCA3BB8CC3313C14005BBB15E68EBB4FFA91BB3B97613BD809723BD28518BCA391163C7FD7E5BB1C02DBBBD0CD0D3CD79146BC2398AE3BA7BF16BCB0A1E7BBE140E5BCAE31D33CF03020BB5E27BB3A502156B946DD83BB877804BC5FEEB73CDFF2D8BAE8C81B3C05AA233DD2DCC53BEF5964BBEF9AB23C16C14D3C964896BDE6AFD03CFEAB8BBBDA2275BC0BDFE03B848BD53B4BE6123E2478283A9111DD3CDE240F3D7D7013BC093C863CC001F9BA4B00153DC67E793C4E63F83C21A00F3CE615DFBBE00C0ABC36C8963C56CFFEBCFCAB11BD161E3DBC4EABA63D8402A63DD2F06A3CB52D36BD7B61583C2340A73BD46C223D3155C2BD840ED8BDD7116FBD364C1DBEBA4082BBC477BC3BF48839BC6C7DEBB9F6CB13BCD96A30BEEBAE093DD0D586BD6FD3033CC35488BB6AED32BD828F123CD28DCEB9F6CB3DBCC9DAE7BCD84FDEBD5867A83DD66B853C988F183D991F1A3D7606C0BA1370C33B47C277BD8E72E5BCFF3F43BB9D4D0D3BFD3D083DE7CEF7BD355E0DBD95960BBB1ACDF9B9FBFCA73B195391BCFBF7A9BBD38E5D3CBD931CBCB36335BED9CA213C0E2D26BBE43C69BDA2AA8CBC846552BC156714BCA10883BCF92B4FBC1E580E3DCBBA363C125B303D8ECCCCBCB5D7CDBCB305E03C96B1553C9097E73B005627BE59866A3C9E83C1BCDF4DBEBCD0EC553C136201BB879F81BCC0B6113BCD2B323D383D8DBD59D8093C573D273B5DF13BBC3BA422BDD27A29BD4EA09ABE4CC55D3CAD219ABC5083B93CF3B8803C3245ECBC03FEC3BA325269BC53B7503B3B179B3DB420A83D7785BE3C22652F3BB89ED3BC03FCCEBC46F4CCBDA155E6BB7D003CBC335CB63D49BA6A3C274559BD21BFEA3BF9AE3ABC8D5CF53C73DC4D3C84E35F3D7B6CA03C710D6A3C9A4C46BB2934B1BC78052A3DCB0D02BDCFA194BDCC6F863D75CB84BCF30E6F3CE998973C1437993BE454F43D4AE09E3B6F6972BB78ED8CBDCE07863D2E1519BC261D463C91A97EBC51EAAF3CB8DB25BEA3F6F2BCFD84E53B4E679B3DA7FB633CB3A5ED3CF22ED4BCBD13883D33CCA83C1052A3BC67E8F63A00D492BBB9B99EBA6BACD63DEA272DBCCB5774BDE2E066BD656C20BCA887FEBB935E703D54C52C3BD4BD26BE9526603C6606B53D1EADB83C4FCBAF3DA7BC80BBD51CD2BDCDB138BC7B1A91BC534809BC9FAA64BCA2D2ECB93E75613D023880BC82BBAE3B8D8FC7BCBBE8863C7C0BC73C43127CBCCF59B33BC0D115BB6D264FBD986682BC9756FABCF55FACBBD88311BD51B305BD08576B3CEC7B0CBD0A4AFCBB5F7BCABC8D8EB5BB6AFD0EBDBDA10BBD52651EBD60DD373BF45AE13BAB0AADBC9EC1ED3BAFC258BCF393343D81D05DBDE3071CBDEBAEE4BC7092C4BD50EFE33B6414F13CC54B5CBC83F1D3B825A39C3D5BF3B4BC4CDBF5BA9FE7C93C65057BBC44E4F13C236DE93C5E30C43BBBB9EC38B7190FBC68C5CABB1E93F33CFB3847BD82FA073DCE4BC53BF4A891BBE2963D3DF73543BD3FA245BD7C826E3C211BD5BB89DEB4BC4BAAF23CC993973C5B7D3F3D0BBEB43C01A110BE659C9E3DCB0426BD09E3A9BCABC09FBC0F57B5BD6E0982BCF76D003D6D17EEBD77B6DDBC9DBC6EBC84A33FBB2546CC3BE108243DC2FE943BAEE18ABB5EA67F3B6F6A00BC4BB604BB9E30E23C04F29C3CE46EF43B788F2BBC6798F63C7E24FBBCA62FDABA478FACBC379319BD4968323DD9B83FBCAFB8DBBB6F3FC2BC0C9C1FBC87E922BC348D92BCEA601B3D982B1D3CD0A214BC7154F83CA18C903BC184E3BB04580D3D8E2C8E3B30F41DBD786646BDA0153ABD728C4BBC51BEEBBC52EAD7385DE4893EA2E7C4BDF7F20ABDFB0A9D3C9126C23B60BA60BDEFA14D3CB1D1853B707430BCFA391ABCC16EAE3D7BA1783D759D3B3C269DA83C3754D5BB144D253DA371013C5DD5A0BDF338793D741F10BD2575DBBB94A057BC66F952BDE173403C93E924BC887386BC4E873FBCEDF55B3C1674E03B0A0C163CC556DC3C7DD7313DCE7A163CA2F00F3B44B5893CF6198BBC8E8D53BE628F2B3B767BC2BDA2758EBCDECF933D8C456F3CE7087A3C2778E2BCA274D1BBEE94AC3B93539FBC99A16F3A299AAF3C83DC023C52930ABCFF1382BC4060C73A9ED7053C0043773BB220A7BBDA8A96BC07C388BC431D91BB518087BCF24787BC58CF9ABA256AB5BBAD855DBCFCF899BB2A2BB03B151F6B3D9BDEF7BAA38816BCA1F695BB7F1ECA3B8C22EB3B5D12C33C61B4F73CDDC6BBBBA33FB6BCF9C809BC04C58ABB065CCD3BFB4F19BC200BA0BA0F3417BC0CBA693D8B400BBCB73F8ABBE541143C9BE8AABC804EB0BB0432933B868B863C7E6F62BD544DF5BCF00817BC1A7F363C30B99EBC336A87BD27232BBEFA7F693C59DAA73CA69549BCF1CB02BCF1EC57BD5ED1B93BF1A285BC9B1F0D3BBD7C293C396A37BD10471A3CD3C8483CB32CA5BA8026F7BC7B0570BDD2DF7C3ABB60BDBD9AE57D3D8F988BBB1740FCBCA6F8FE3B38B816BD543B5BBA6C6FAFBB74DADEBB0A4638BD88BDA5BC972DDABC3CB3443B8A76303DF25206BC4F7707BED195733D212E1FBB9E27A7BD4EEF10BC524F40BCE913003C3884A63CB7A1B3BBF3FF34BD03BE4A3D667A9FBBD31810BDA4C3E43C202B01BDBC9E04BD18E9B13C017083BDFA3D313DA3C8463C3A37E0BCEB97D03C2E864C3D5B7150BB044595BC5153ADBC84F57C3CA3DCBDBBEDF832BB9822E93B4755C4BB915C0D3B5B0DF83A1030F73AF611293E1134B63B0018CA3B26D053BCEF83443D15469FBC1974B2BB56C6723B62FBE93CF2E5283D448F723C6C2120BD5F23B33BF0BD14BCA70505BD8FFFBF3B8FE49D3D8C6E9ABC709DA4BC489DF33C41D08FBBECCC09BC70390CB9379DE63C6A2D0BBD2A7F853CBD66693CC01F793A96DE85BC6894BFBCC741ABBDE88B2C3DE6CF9ABCABDFA7BC7B3F88BAFE9A01BDD75B5F3D1E71053D21C6723ADB02E0BAD4D668BD3A9C65BBCA19FA3C764A08BDCA5059BD9CEF0DBD6A7FC33BC094B43DA9FCDA3DB46283BC779324BD405CB03CBEB8EB3C63A0823C22F78CBCCCCA41BCDC89D13B4C451CBC8AD595BC553F163BFA8886BBFEFC49BC11F4B5B9C6D3B93B2425B23D2F10B13C7C97233CD0B97DBA3829BB3DAC48A5BC8B28D03CFC0F1A3C7A116BBC191D0F3CEB8A953CCF9981BC41C26DBC0E14BABB8AE2E13B0D69343C2E4EAC3D36AD3A3CF98B943B3C68573C3B84D1BBD14A81BB340388BC915549BC27CABF3B3C56B5BB0AE6753DBDF9EDBB4FF2073CF3929EBD9C7F733CA485EC3BCF0D2DBD7FA3A53CD7CA40BE75978B3C92FDBE3D3EB0653CECDF1D3EAE2E1ABB5D0EF6BDAFD35ABC7BDA88BC073DA9B95C72E1BC557C223CE129A53DAE9717BC9590323B2CEF37BC368A233B2B1E18BD9BAD593B704D713C55C38BBCDD9212BE39C28ABC894280BE671620BBFC6003BCD46FC4BD3E86953BE00C203E54F534BE002B803C3D94863CC7EBFF3C04285C3AC22A943D3B58653CBB85B339CC0A023C4A1ED93B66BA8ABEBA0ECA3DA1E1ACBBA9BA0E3DEE1D483D1A87BF3C0B15583C05D1D03C40F8303C8CD7863D89FF0CBDC45D8A3D2638F3BC9A6E73BDC2E88ABD10EF94BD6DFC833DE29BBBBC0062983BBB254A3D8A470F3D1F258EBCBD3C4B3C994A08BE14EC3ABDDE43D9BB3E139B3C453209BE52BE15BEC37EABBAFDE02BBD2A0EC73C71441E3D745D053CB8F4F8BC8F15B4BD12FA98BC9727EA3C8300B0BC2A7141BE70AF84BC19140BBD905D713D436E4F3DCDC44D3A3ECE7C3C263FD93D33568E3C34C07E3DF4A8E93B5F3D333B7978DDBCFE5E8ABC7AA9CE3D1E4D1EBE86674A3CB342443D6C27CB3BFD55F7BBFBBCA43DCF932A3D5F5394BC453124BD304E153D6F4E403ECDACE7BDA010FE3C240BEE3C81BDD43C223DA3BCE5C4343DE03E5B3D5AE7E53BDEA6DDBD368B3EBDA86A11BE965CA4BB9C81883C65C485BD2BCE13BD889252BD4183ABBCF01797BC88D715BB017CFBBCA33D883D7D41003CE3C50DBC0C6415BDAC283F3C7638A93B57FBBA3C24EFBA3C4565BA3B1D64413D5ABF33BD4C1721BD5EF4CC3C300BB1BC557F38BD9EE8213B83B31EBC14B45FBE937AC73CBC2B4BBD23F9463C9130773BC938E8BB0ABBA43CB64015BD141AC2BA1C4C903BB0008DBC3B5CE13D252459BCC1CA1ABD2CE02C3DE612BD3C55CF3FBC167487BDC1CB863C311886BE3A36E6BC055FB2BC4725863C9AE637BDE85F83BCE131A53D8971373D9D60763C8645B2BC1C73B9BCCB5F553B9A94343D828CE33B675DD3BB5905B1BC4AC134BB6C90BABC052FDDBB9ABA813C7FCD79BD78F73DBCC3BB143CC41259BDF2CC333C51D9F6BBCA691CBC75A4AE3C0A5D15BED13899BC8811983BC49004BEFF79F3BB03ED1EBD19EF4CBD43F7933CDE1BA13BC65CBC3CDE1E1E3CA307ABBCFAC1E0BDA92C93BB384C35BD2B0F77BC5DFFE8BB719D05BE2F0025BDCA84A3BD704004BDF3EF3D3B08E7C93CBD1E84BDE5BDFB3B00FBB2BC5115843C47AE85BCDDB771BDA349243C8CC98DBD0D01AEBD3B582B3CF45A203C1555663B7979BF3B8F0B4BBDB970C83B21666C3D5248143DF234CF3C81E1283DC09CC4BE9C30AB3C3F94733DCDFA08BC1468C7BC7281973B5A4512BDAFA6563C0947283DC5359C3C9BDCFDBC57D9E1BCBB0F30BD04E3953B9177A1BCFCA226BD11DD973C69773DBCDDE18C3C91C5D03B133E89BD5412893DF59BF4BC39460ABE9ECAC3BC19A65CBBA25A2EBC7A7380BC2286093A12483BBD27B8D2BB884A5D3D72C4AF3DFDCF9FBD5F9C9BBDD857AD3CF4D7133E9586FF3C13EE483DF9CE893C6A992EBDEB8E0D3DA9BF09BE188DFFBD1352DCBBD110833CA359CE3C675BC7BCE57441BE9AC546BC92B511BD843D333D4EDA6F3C0F7398BBE7C57B3B9515A23C2236CABD1B0BFFB9DF2FDE3C4E53FC3A66DD723BD095793C68BE2A3C138EF2BC10E5BF3BD0638FBB9AC08A3BEA61863D287A86BCB9B01B3B77BCE63EC057893CDC5FEBBEC2F0F7BE561106BB3268EF3C5ABB0CBC4CA7CCBC70B69EBC6AD2B83BF1AD14BEA217CD3CBA7D683B7EF0493EEAD02DBD2781CDBC4A2B213CD11049BBAE88F8BD2B4D0EBD3E2C19BDD0AE9ABCE60B383C0D116FBC35F4523CBE4B733C1A337B3C4917D0BB4DA237BD2B08323D5C1895BDBED64EBC4989D63DB57A103D34EF2ABEE1A7C4BD3D0792BCD2FA7DBB96AAF9BB0357253C5F47263CC706E8BB85099EBC8099D8BBBB349FBCC615D93B581A41BD658A50BD74394B39C353393D8FED2DBD9E14AF3A9611353B68BC17BCBA3ECABCFA9C413B4C231CBC28ACA63C9F4B9FBCBF71873C44DEEEBC0D822FBBCCB8843C21069C3CA319083CCE976C3A6324E03B3C85A93CAB0B71BC176A82BD9EE821BC4E6D16BD5A6AE53CB10F8BBB0D99A73C33E077BC5C7D73BC12B330BC1D2828BEF17E3EBDB263A4BCAC12C2BDADB1503C6E90363C159E70BBA02220BCF67D37BE53A86EBD1BA7663DCAFF613DDAA8E43DB9D1B8BC87D20D3DE4B68FBB5D169C3DCC4AF3BC9F6C083C7D7DE4BD8041C4BBD92D03B9F4742ABD8B0A533DEE1D5DBCBFEDD73C264EF5BCE81A1DBBF9D371BB64E321BCD7AAA5BC4E402D3CF13065BC651725BD577ECFBC22530DBD8FB8973B299D35BC7D859CBD15A3F53BE85EAABCB5522A3DED3EA8BBD5D9A3BC2ABB8DBDC0FCABBCE851073C8211CE3C972C50BD6BC6B93CF3B5413DD4C0433CEDEAD1BC367BA73C0365133D9A10173DB89224BC84A8843BEA804E3B7CE0BDBB47353BBD30E49D3C41FBBCBC742762BD4DF79F3CC9072FBC68B3E3BBDF75333C341C28BD6F9D4DBDB294A1BB61FE51BCC59628BB31579DBC43C936BDFEB63ABCE65B7DBB2CC307BD031E83BC86F416BE1723B5BC22B76A3B2D8B03BE97D86DBCCC96413D17DA073D160851BB8B950DBC18DCBFBCF9824BBC5E2C71BB9CDF31BDE74DA63C98B0F0393674DFBD6A2B473B7F8107BEED5C40BDEE0893BC7D0BB13DBD89AC3B64086A3CD75B8BBC693B113CBBBE1DBE855848BD6AD66A3D73CB423C229A1C3E089049BD064EDD3C0D52203B43351BBD22DF013D199BEE3CB09A8FBD92CD48BC09DAB13CD092FEBD33FDB8BB6CFE6FBCBC129C3C7071053A6EA65DBC992BC2BCF34D593B92D4BCBCBB4D393D26B120BBA84000BAFC6F543BA164493C595C62BC8B13843BABA3B7BCBEA7E5BC86BF3EBA7DF582BB15615DBD04C66DBC53C3F63B1D3CBB3B9B19B5BB5D998BBC2422CBBC20B1993C79B951BDE907C23C8798A83C5EB2B73C94AED03B2A978FBB2E21F13BD47854BC566B673CAD0459BD1FAD95BCD35B733B826CF2BC10FCE9BB5787793D972A8F3DE33733BCCFF31CBE8ADCDDBC2968ADBD59AF73BDFB193D3BB8C1073E7F416B3A9CC150BDA64BEBBDFDB73F3D64C745BD483D233D65C65DBB85FD3F3DD3CA8E3D3E7FC4BDDA39853D458B133D364B56BD0FA8083CEDF6B6BA638FDF3A81C222BD2C575039612A7CBD1C0C4ABE50C2093C994416BDE4A4F2BD19C7F9BC61E426BD2B1E943A62B3BD3C3CE885BD41F1E43BA8C7143C615CBC3B8156BDBCEAA9A93C999C893C8CE7B93BFFF23A3C3F6809BDA91F16BDA42759BED2B872BD584DA3BB4365BF3ED51AC63CA519DBBEF4AFD7BE15A3C2BC304C323CE8C851BCF1DF36BDB1AE233BBD03173CE0E389BB994ED03BA2555FBC01C6743C6C9D52BD9993213D672B833B832E5DBBA73FB7BA43A4B3BC39A2B63D02F2733CE7589DBD58F533BC1D8AF4BB978CC43DF3B4023EA66540BDF4D366BDAA32803CF8F4B0BC8A54903C0AB906BC1F01A9BDEE0FFCBC4901A83C75939DBDB21B4ABE92A3D23CA9E0833C8A9709BD209E69BC263D0A3B039A9BBC0A0D05BC0079913B6D99B7BCC7159B3D27B6233D531982BC0EEFEE3A3AD633BDF5812F3B7AB7F13B8C936FBB6E94DABCF372B2BB150AAFBA4101683DC625873CC2290EBD07B5BC3BF94C013DB7B72ABE27E433BC9DA815BDAF60373CEE5FFE3B9351643D208A6ABEB91E283BAB7AD5BCEBCDA53ACA60813DDD773BBB127611BD480A51BD4458C0BAB26FF8BD12870B3B9156BCBCF38DEF3BC85FA7BC7ED8703DE3FD2F3E2C86CFBCD8F0D03D8B46033C0C92D3BC8502953D2AF232BECB6D11BD1B36813D7228B03C6CCE893C10A51ABC4586A13C71D72DBEE4249B3C9E2BD1BB27CE1DBDCFF8363EBDF72ABB6C8C5EBDF25B06BD6688F83BB3259B3A9C6197BC67BD8EBC52A3783C74A24C3DE8D9F3BD1454A9BCE1CAE03B90CA9F3C68490A3D930033BB6ECA88BA9FE9CDBD40E9243D3C5DC53CB2CB2EBE8546F13DF013263D279973BD4527DABB8C54A2BB3911A7BB274656BCF959203E93E19E3C59215EBC295B13BD7F0BC83DBC590D3B7D77BC3CFBCC133DE09DD2BCD203CBBC2E018F3C45541DBD9128D0BC90064ABD647A0C3DFC1B383B2B592A3C5C6EBABD73D9873DA37771BD544A6DBD51DC4EBDAECD5FBC9F414EBDA8A8B63DBBE4BE3C691154BCF3032BBC8B90253A101FE3BC241C53BD540B9E3DD72CD0BCF47BA3BDAFAFAA3D0FAED03B0E1607BC739F6C3C851DBEBC406D163C976FC1BC675BE6BC986D533B0CE67CBC00B25BBC15711EBC34073BBB1CA1353B2BCC1E3C004EADBC43201A3CB79124BD449719BDCBE9A2BB8537A9BBED2080BC1B5BB8BBB572AE3CF155D73C24C433BB6ED6C93B3BA9DFBA8324A03C9E8793BC1E9C773CC98DBA3C5362093D409F973CF8812CB7F293623C5B42E13AECDD83BD9AC0EEBA107F07BC70B440BDA440FF3CB33EB33B35888CBC61AB2FBD07005BBB56093CBC6F9309BC3ADA433CE733873D5070F3BC16DE423BAF4AE5BC9E61A13B9B500C3DA0007FBC75B5A83C2B1277BD12A7FAB9C03AAEBCBE5CE63C25F9D63ACC62D8BCC8F9A33C7A4DABBB6F271DBE119276BED00FFBBB8B8B2DBD3C20743DF773ECBC712B35BE35B96C3DFA8702BD91B0413E1DB2CB3B0C2B8DBC7867F63B4F62533D9064FFBD31F672BC00564B3C6F029B3AC8E806BB6A092D3DE826D13D3CFA4ABDDAEC4BBB013F003C2319FD3C2D44953D174E503C783B863B698CD43EA67532BCFE3EB9BE97B400BF846AD53BCFD5BE3C5027E9BB4BD26DBD62DD90BC7A1CBABC50FFD13B1891E6BB151A30BC5C7D3B3D869C1EBC1E6FF0BB27B79C3C0319573BC49929BD91FCED3C9BE7C7BB5ED87DBCF2C31ABC405D8ABBF48B77BD99C04CB9521B1DBC5526A1BB9656FABC5EE6B4BC939BFDBC378585BB38363EBD050E863C427A503D265D813DD5853FBC6750F7BC4DB7A1BB0A475BBBC9E794BD70159FBC74506DBC570379BCC72B82BB072CF53BF723243DC47C813D5DF8B9BC1E33AC3D59B2A4BCEE1858BC199D33BDBA765FBC959A273E5E38BD3CF22314BD1BB527BDAEFA8EBD98DA5DBCB60B45BDFF629D3C3EE49CBDF7D437BD21C26D3DD237A33D03F500BD6B1E283DD56201BE502221BD249FA3BC1848453DF9E29C3CA57306BC185E14BCAEA8D83B4F9205BC60D983BD9A73BF3D8E0AF33C8EA3633C737F953C9BCFB0BD0EC1FFBCE0D22BBDEBE80FBDA71B2BBC035F353D7C4D31BD4AF3C0BD061085BD50939BBC940CCF387CDA6939C702593CF285FFBC0B199DBC7D2BE73BDC6D033E44ABB6BDF84ACA3CD5A31C3DFE4E0C3CF1CBC33C94F9EB3C044259BD6763EB3CFF3CF2BC60E5EC3CD4B551BCFB8CB9BD3D24713D182A8E3B92D92CBDA52E74BCF46DADBBBCEBF13C7B3263BCEB4A143E75C28BBC5A75DB3BC09F053EED5232BE4A03083DD62EC33D637E71BB8A3207BB61A37CBD3C4915BA781488BDCB43A9BC78098ABC2999EA3C6505833C0CCF55BC87F1B8BDF7A466BC9285113D44A669BCD67C0D3C2CA398B973F1C23852DF0D3C38E2DABC534D923BD61883BC05CB5A36B38174BD04F5A93CA1E897BC53609F3CADCEC0BC44FF143C2CACB33BDB4864B9E7943DBCAA13D6BCAF99AD3B0FF42FBCDE1280BB0CC7AA3A57370C3DDF1461B9DC84463D7A3E06BE437E84BE0E88303CAAD6ACBC20BC19BEB4E43FBA425D14BB5A966DBD63373E3C440103BA0E59153DD793263D6A1B89BC36D606BD8639633B54415BBCD93DD3BC12B72A3AA33D3B3EA8973DBDAE315E3C59CC39BDBEC891BD564100BEDB33CDBD3FAD13BC974B34BDBD980EBDF246AF3A1288043E585F823C760513BDC7A911BE0F8657BDBCD131BC2D07823D08B391BBB9909EBC8FF2283B038D7B3C589390BCC80E2BBDFA7E133CB6860ABD561E0FBC00E5363C88E400BC691B08BC3968FEBA044A6BBCF76B01BBDA54D13B6378E8BCAEA889BCD71B03BD07322CBC0B7CDDBB458808BB843E34BC0E5FDE3B29DF42BC0BE4C73C3F46143DB7AE9F3C4BCBB4BCDBFB16BDCCC189BBE3963B3CA040A23C49403E3DD034AC3A376B443C9C8757BD0B9BBEBC002E0BBEDCA855BD0FF833BC0B8157BBC1AE683B40F4F53CA7EF143EBAA586BD8460CEBD1BED62B91FBD0B3D0AF6403C52027CBAB3BF86BDDE93433C876E183A1768F73C22A5D0BBD20187BC87A4BFBD2B0E2A3C324B343C904176BD428A033EFCBBD53BCA659E3CA3DB31BD9DE8DF3BF65EF2BBC26C1ABC234EB1BCEB9E8DBC27D4F93C798A1EBDD7F11DBC36C340BDF1113D3C207119BD2FD1BDBCEE60B2BC74298C3DC4A5043D3ADA7D3D988508BE543336BEBEC718BD2D503ABDA4D3BB3CD69D043DFA1D3EBDB4AFCE3B96E5A13DC30D973C1656CE3C03CFC7BCCB83E03DABA5C9BB77C39A3DF2B08F3C6C1A533D97F5A7BA4AFF4BBC4B2B92BD9695FB3CE1A5ECBD6E1D1BBD5FEC22BCF61F273CC016F23CB83F153CFB67683DC98DAFBD2AAD1EBB817ECBBD8FA92CBD832D2A3D00C364BD8E3A72BCA601213C33F99C3A053CBEBCB9FDCCBDBEEA0ABD522CBFBC84034EBDC5E8893CD6F3B7BB5AD4943C5A7A003C37C06A3C90D6093DE009D8BCA42835BE2CC089BC8D1C25BD881DB1BDAB57CE3C92C2613B0371B63C83B80B3D4ED5E8BD340D873C49A4AE3C165239BB71ACC33B6846AE3B6A8EF63CDB7D02BD5227023B470508BCAD3EB63B2C1094BD7A4630BC1DB56D3C0541F13EF2B6C63B0FBD02BF2A2A0EBF9E88273CCAA86F3CB7B900BCDA88EBBC0C76C03BFE719ABCC581B83BAE0556BCA5222D3D7DA396BC0ECAA1BD374AA6BD82B03CBCC118463CF1161B3C3467583D07A1A63DE00BA3BB661F883C23AC5A3D3CE7293D6C32063E39A6FBBDAA58663C12AD263DA119B83BF77A4F3B1EBAA63C4558BB3B2FE92CBE7F3661BB623BA93A50D144BD1BDE8F3D3C62633B768100BE4FE61DBDED6DABBC834300BEEBE900BB42327DBCF08AC6BDD005CDBC33AE26BDB2668F3C461F85BCC966BEBD39F9BE3C0283203C864B3CBCB5D2ADBBA3D4FBBB8A5609BD69D2A93C0A5B11BB2767F2BBBB5B54BD76C5E7BB1CA277BDB985073C26BC8A3EDC3D89BABDD88BBEE1F560BED850DABA82AE443BEAF06BBBE3B8673BD457FDBDEE16A33CED0378BCA7A179BDFA2C1E3C8C7265BCD3853D3D4BF396BDB827F7BB9737763CA2390B3DD8E911BC44DFDCBC34D295BC3BDB2DBEEE63D43DC345C63CA07D86BDFFF2C2BBDAA492BD18F0E3BD2964A1BC3C245ABDE911DEBD2CF7EBBB6E838C3CB7B7B83CC8A390BCD26A413D7B84443C6714403A32BB2E3DC7EE9B3BDDBBF0BC7C7E64BB8EAE27BD8BFBDB3C0541343DF03DC0BD7BE8D6BDB4C181BC229C483B02244E3CD143C63C01024A3D10A9B5BC32B4D83C4990713D10A019BC2BF20E3E3714C3BDF94FD23C00093A3DA7A62A3B55AC973C0FC0373DB05979BB18A20ABE17E188BAD32B04BC932027BDF92D913D92E692BB1FE708BE8790A0BBEF9833BD16BC70BCD184A0BBA512ABBC9F79BFBB77223DBDCE0CAA3AFED7FE3BD47D23BCEFFEE1BC4AF5B53BCE959E3BD8D91F3CECA504BD5EA1CCBB51C3BF3C4CFE633CF60A71BCB63EEABBC8FC6E393A8C05BD9952A9BC9D55463CBA558C3BD7B060BC7F67FF3C2FD9E73B9D45423B02C534BC8692353B7F5FE6BCECDAC4BCED688FBB0DD886BABD0C2ABC1F5CBBBBE9C4333DCDE7F1BC72D381BCD866663BB1A2913C984C3DBCB5E1EE3B27735ABBF535A3BC310898BCBFC45FBC3A8932BC99A98F3CC65631BBC259393A311B4CBC16BFADBB8DAC99BC0FF5A63A04FB3C3CD4732E3A853D3FBCFA98BEBA0A8A32BC5AD953BB665CE0BA81D28ABB29D95CBC45C316BDFFD59CBB1DD306BB9DB1423D7595E3BCD7BB793D1C08F2BB7B21CA3CD9920BBC3A9F84BCB9971EBD17B0F5BC26BB053C39FAEBBD1A8F843D350CA7BB71D9C6BD4A8B8D3E71219F3CFC6182BD7B2E4DBC8B5F143B912DA4BDC8E6FF3B3FBA783AD4BA673C9E437C3C4D32EA3C6A6017BCEC8BCABA146A6D3B307106BD3E3050BBB72DACBC5C540FBD14899CBCD8DEAA3DB29683BDA56920BBE00935BB19B2C43A73BA1CBD8392B9BA97E4913C1E50C53B748F153C0DFD69BA81DF10BD802A943DC7DEE13BD294B9BC3F4126BDB83C073D6F1DA7BDAEB58DBBF280133E2301C13C644381BE172BB03D524FF1BCC9F45C3D5B8B81BCF45D79BD3A32363B3B3A433BD9370ABCE22B523C8EBEA0BB37E11A3CC10C36BB786989BC1F0C9DBBC4E6DE3C86BF8FBC877675BC1502DDBC4E4707BC633514BBE1288ABB14A6CBBC0793A5BCAEC1573CD873D33B777F03BCC1D8943B34708DBA6DFF803AD2D2B53BE263763C8BEE6CBCA33CD7B7ACF61DBB50572FBC89CAC9BBEF298ABC98F882BCB0A0343CEE67A03C42D215BD8CD07B3C518213BBA3EF8CBC1BAF83BDD4CC08BCE50CB0BC864865BBAD9028BDDF1A373DBB0A5C3BFBED15BC56DE6E3C0CB589BC8998213DF4C461BC14670B3D456087BD8A5FF5BBFFF1073DA33CB33C3EB12BBCDC94E4BC51B307BBCA1893BB3D22E1BDB6464EBEB0EE47BC27F207BDB0D44EBDC495DB3C01C6DA3BBD278FBCD9E37B3C3619A53BED72C1BD2E206EBBA7DCE8BC9991A3BC84F8BCBB8309E9BB3ED26ABD0A6F8BBA78728EBD4F1C463C219DDB3CFA732A3DE21F98BD189367BDF28D543DE95F593AFE824F3D937C23BE83B2263D9C7541BC0A7417BD79DA9D3C34C9043D0BC822BC4205BFBBA17122BC27ADF33BDB8222BC62CAF23A62F6873B5C57953DC5E3183C76DB00BE77C6EC3D95FFB43C80B76CBC03FDCCBC26F8BFBBE43A263D7D39D1BCC09F0D3E743734BD0CAC833C9FE1E83DCF2717BE6C05F7BB6D24BE3D644DE83C3536F3BCCCACDDBD297112BC5D4E84BD30EC4ABCF827BA3CD913043D72A6AEBDC454A93C4F9C5CBD69F2F2BC459C643BF1E45ABC943482BCB6DD75BD879CA23C00039D3C818785BB83B692BB66D5873CBFA8593C3EDEBABD2FB548BDF603B33C41042D3EF4B988BD32371BBC7CA093BD8506EABDF188DCBC5895533D4B93763C69FE9DBC60B5E2BCD81D92BCFA75313EC9C42ABD6974AE3CBECC7ABDB59B8B3DFD75A73CA5ABF23C5CB1AFBB5A33C7BDA9E1B2BCF8BC813C2116BFBD1B2E363BC779753DB965813D37C1413B0B26053EB31A18BC6670A9BD2A446BBD3D3B0E3C75A29A3D344D3DBD563508BD563B3BBDD2DBAB3D1396673C8D84F43CB017073C4592113DD5A7C23CC096D5BD7DDD223D5DFEC23CA45F6FBD86C1DD3AB7F2A7BD9C2F8A3C0783FFBCEA6E423DC8BB6EB9AAE515BEC7784FBB89DF2DBC4B9D19BDF166863C845379BCF21286BB785B483C954235BD7BC062BDA8D012BD06369ABC384D9FBCE06920BC266B9D3CBB5B5EBCDB2F77BBC17DAF3BD1A7A8BC225CBFBDFC02B6BCB31A2F3C21DD603ED218353C7B0794BE343581BE4D91C9BBA04F903C9798A5BC6772AA3C9D13273DD1CB73BCF284ADBC520C9C3CC75485BC2D5011BD5909BBBCBF47D8BC6D58C8B9BCB9753B2BDEB2BD1D28523B0D11F6BC2EDCBBBCD2B7EFBC4A9665BC7E9529BDEECD583D2A4E22BCF45AE53C4BD78E3B07F62CBCBAC823BDC5573A3B48DB06BCEBBFF4BC05FB1D3C17D7853DA76707BAA83BE4BB37258A3C1E3B4CBC6E50B5BDD023DBBA714E863B0EA899BD38684CBCC1AA683C1CA90DBEA49054BCB0B010BC157F2F3A5E93FA3C1B361D3C4939A63DF0F643BCE27915BED64117BCD3E1BCBB7E14F83C6A0DA23DD27216BEF7CD303DA9BE03BCB4D2A4BA9532073C5AB99F3B423734BECA1DB13C067934BC9EDAE0BDB358003EB7B515BC1E371BBC50FAAB3C890FCFBCBD8179B96469203D3F8721BC7C39FCBC67643D3CC6052ABD046DCD3B36BA7F3CAA7C58BD88639C3B05CEB7BCF4BE87BC739A30BC65DF7D3CF64A01BD891DFCBCFFC3043D7382013B04197BBCB83C883C1ADE36BD0E9A49BCC65B533C3450B73C847DF43BD5994CBCB087DCB950905F3C01D903BD00EB6CBBC7E926BCAD3ED23CC0443C3B0AD24FBD31B46EBC68FE443D73DE403CBF04C9BC2957ECBC502C1C3C29C9A73C1402B3BDABF026BB531E2EBD8BD623BEFD02D13DB3DA87BC7A1F1CBD6C7FFFBCFAC806BD50E42DBDE977CC3BE7578D3C530420BE46F961BC6CC370BB684D81BD94091F3D184FF93B4A1B6C3CCE24763CCE9A9FBB25AC3ABDF8CD91B9F4110FBE9E60A3BC840D99BC9B74B0BB84D90EBD316D78BC9C0F5DBB1B3C89BBF1EF98BDA61D3A3D8444863BF06FED3B03921ABBF7507DBC18DB3E3DE17B4F3D212D233C8FD5C0BCD9B504BD4A3CF53C057E27BD2AA9313C8BCDBF3EF3B6893A0C05D1BEF5EDB7BE3B6910BC1F44A7BB6DA3F6BB7BD224BC0A38C3BA5BEA8F3B54E23ABC2ABBDDBA9A5A53BCEE38153C9515C03BE4C70C3C9B33AA3B9FC4B6BB7416EBBCF43486B96BB2A93B012A94BB27176BBC5E315D3B2FEFA8BC562D0CBD1F94AF3BD744B53B5B68D0BB1CBE9BBBBB63D2BCF37B88BCAFE1753CD39E243C08F994BCE9108E3B38A8D03B958B0BBC972BC53AAEFC98BBFE9F63BC29A4413DC66D3EBC087983BCFEDF0ABDD57C4BBC16BACC3C37948CBDC5C536BC1F533FBAA094543BF16D73BDC6C80CBD3A9D95BCB7D928BCCDA0ED3C1747863D54B222BE7D1ABBBDEC8E7EBCBA3A06BBB51AA8BBB4B5C03C8CC33BBD54E67A3CD7100A3EB1F1863C3E81D43C6A7C63BD93F3293E9099263C4ED7503DB44EF1BC87B9C4BCF351133D524B05BD0601793C358F2ABC085D6B3DFF131EBE82C3CFBAC5E0ABBC345FEDBBA276DD3CDEE8C6BC3163D2BB9AA981BD57BABE3B82A3C8BC669E2BBEF977153EE04F603D109BDABCE065E3BC9E4A853C4480B03C469F06BC4872243ED2B48DBC633CA5BB0E69D9BCB01F753D24A09ABC0B808EBC7B5A893CC9728ABC00F78E3C5CC768BCFA1B49BC3394FE3C4BC49FBCB519F8BCDB52AF3B4EAC433C7A046FBD8025BABBE673D5BC4F96DABC6678223C462798BCBC1CFDBAC683D73CCBEB853CDE4DDE3A7210903C77E8BDBCD4CA22BD3719023C52D1F83B7698D43B320A49BAA3342F3CC085153CB86EC63B058770BB021F35BD46C740BD9E4FE3BB4012CB3C1E65B1BB36FE7B3CB1EFD4BC388F2D3D9B86EEBDAD92073CF2991D3CDBE427BC722C13BC199483BC0D29193CBA72243C7226643C1A49E6BC51F2E6BC1DA86EBD20466E3D7CBBD9BA4F12C1BC8F466EBB18CD273DF054213CB5010D3E6506FD3B1F4E57BC76AC57BD914CA1BD03DCC23BBF8AFFBC85FF923CDB6665BC223CDFBBA8B4DA3BF156F4BBABA3163DA03E893C738ABFBCD4DF923B9795333B52DA09BDF3ED23BC7077CFBB6B855DBCCCBC67BCBA35133A3E3AB1BCF70BFABC27218D3C257AC23C3DA04BBC7F0E4F3CF6FBCDBB41B5A93B40D972BB80138C3C93EFD23BC632EF3CEAFE3B3C4697B63B8CDA5939A077B6BC9F85233D728D3A3D1DD917BE48B8133DDCFBD1BC3D647B3CA9C4743B43E3403CD744153CA09284BB407D5BBD2A361DBDE5A8E5BCE17E32BC349D963B7A82FABB695FAF3D1AD33DBDE31BACBA49D59E3C3599A33A47DC82BE6E86CABCBA9B6E3B4D5B2E3E0D980EBC23C465BEDBED59BED22D71BC962BDA3CEDA564BB17308FBBB52E54BC536B08BDBC50793C14D2B1BC5FB3CB3D5835BCBB9D153B3D3AAFA3BD16C0403CD919C6BB129A853B2D6DACBDB71194BD36159EBC0C69F0BC20200F3D471F2C3CBB55BFBD6B85B8BD84D61F3BC089F2BA7B4CEF3A5914883C9D194CBE09D88BBD0735133D9D8D4B3C4A9318BD0FAE5D3D424E0B3E375FCD3B489CFFBC6A1C7FBC8B83BEBC94ECA43CCB0F14BD37BAAE38642273BD737603BEF6C5103CE95F0EBD1543213BDD2FACBC4898103D12F646BDEB6737BCC3FE9ABB909E21BA41A8293DD61F943EFFA7033DFE9282BC2985BDBD7FFD833D9AB10DBED34B133C6B65533DAD063C3D209095BD23A25A3EDAD0BCBDA3C9653D307505BD24FB003D449D903C1D1B21B9D9CDEEBC9ED53D3CD8EDACBDCADC9EBB06A7C03B5FE521BD299F55BC4D6F413CC411FE3BA25B82BDFB3237BDA760D13B4AF5CFBD2CE87CBABEB9923CA49A9CBDB17B8EBC6FD2703B7D0ADCBBB5492E3C34BEEA3CE4760F3CAF68F1BD946FD3BC0BD4FA3C4E0355BDAE2D483DC18F263B758DF2BB380CDBBC898028BBED36D3BC03AAC73CBEA8C4BCC2E7943C688C94BC7C51833C5424783D4BDC453B761C19BCA97551BCBAC654BD17A3BABBA2A84DBCD62D42BD4246893A05F464BC4659E6BCFD2A503E9FB6EE3CD7D478BD0020B63BDC5226BDFD19BABD483F5ABC200410BD40EC31BCE99F2BBB550D1A3D56BC30BE751ECABB3E4B443C2733A13C19FD38BC57BC66BEEB03D63CF8354DBCEBE002BF1C76E4BA4F744ABC46CD16BCD587723CBD1E44BEEAA059BD4F9BDCBC51188BBC54798A3B51C01A3B0222EA3C285FA1BC0353553C053242BBA68FE5BB9CA3D2BDF43532BD86BD9BB9B2B1843E552C873B631E9ABE1B4D96BE1818A23C6A3E503C7E928DBCD72E5BBBEBBF37BD9475BC391B91A3BCD45A70BD99BD2DBD9865103CFD4EB8BD2ABBB73DED3DF53BC610113CCA2C293B86B9963C1C8AF03CAB14CBBB48FCFC3D74439FBD6F0A60BC85B93A3DE18EB4BD4FAEF9BC795A8D3D7D0E3A3CB52BEDBDDE3CF4BD826B32BCB61597BD37E2B33AB1A909BB13B5423DB32EE3BD1D1BF63B32E2CEBC08B8FABBEE280FBDA4F816BDA307583C9C0A81BC3BEBE3BDC2E237BD69CD94BBE1EEA03CFD796A3B8963DDBCE6D8D2BDF9E44BBB03DE5ABB23106DBDC771B93CC861BC3D08889EBBB716E23A1FD4DCBC0275AC3C7B6D8DBC7E775B3C3C7702BDE4B5483CC0396DBDDABAF13DB159CABDA4725F3C9280EDBDDF3539BC0BAA253DE38F44BE92BAE73AF64424BE0A18F6BC1B700DBDEC44FD3CB4AE683CCD2448BC98CE4E3DF69C1F3D2B89893D11FFAF3BE9F2D93D92630C3DA2C11BBDACBFD23BC6367D3C54F7D03C25E8223A66171BBD9169BCBBAD8C2F3AF32B17BE8E5B8DBDF495723B86BC7DBD4D6AEA3A49F3103CE2003C3CC7676CBDFE7CA9BCE5DDB5BB33D8D0BBBD4EB2BCBCCE43BBA00DE6BAEFC9D3BDBD7E1EBC6168673EA6875FBBB54E3FBC9218F83B5CB8E83CF07847BCC1948B3C25A5F0BC7748DCBB855CD3BD7EA980BC5517943CB11C81BEC2F07BBCF00072BE3D57D2392E833FBC861A12BC4BD9D43B21F1DC3EBD8AD73DAC2FD6BCB9259C3DC3C6103D76999C3C4397EDBC4C7B03BD00CD08BD108AF1B9BC4CB93A87FCAA3DE9BE9138DD983ABE1FBE0D3DF307B03806BA1C3D4DE3593BBFAF223B05E23CBB9D509CBCEB35C83BF083093E7ADB803C00AE74BCBDB98DBD460ABCBCD3ADF0BD931B503DE51C21BD9F625FBB18C40C3CF625023E15AC2DBC3B0E4F3C2BEE093E4AE4F2BCCDCCF1BCD467DC3BF70B30BDDF7E433CAE1AB03BCCAF06BC9589D43BF565B93B2AE309BDC40C47BBB0038A3C0173803D68960ABC7D808D3BCEA024BD818E333C3318023DE5148DBA97F158BB72FDDC3B9CCF8EBC21D020BC8C5CAF3BAD2500BC657810BD0DA4C53C2E6EE23CA94D043D0919C9397C2711BC786501BC025FBD3C13E0C33C3CC96A3B7BF425BE38A2F6BC3E75FCBD6D36E1BD549FD83BCD57D23D764AB9BAC44B4FBCD6B18ABD9FF5F13C88F0803BD0BB5DBCCACF273D3ACFCDBCDD18753B58D88EBB42E509BC3D414D3D14BB743B880368BC6480B8BB392D6BBD8E9F9CBD6F17DBBDCFE5FDBCAC625DBC5D39F53BEDB2133DDAB6B5BB87455BBC3416EEBC126C973C657CC63D952EF03C35A8343C3B8FD7BC3E4DFFBC6F8158BDAC6E133C1773883CF3BD593DC92DD83CC0AB07BC6903AB39C824E0BCA9D454BD4E4B13BD03F9733BFD9BE13B068C9FBD2EF0B2BAB812B7BD276F09BDACD85D3C7C7EA8BDE3DB693DEDB8403D3DBD0CBD993A1FBDBFF9A3BDFF6EFF3BA99DFDBC507727BD7697B33A83F6093C5DC5A5BBDDAD8A3BBF460B3B044CC4BD71C8C6BB9BDDC0BD278D09BCFE11F93C8BC734BD3A618EBC15A0BC3B547A7A3C1F1F663C4B1CEC3D050BBB3B02439B3B4DAEC0BA528A61BCDA798D3C735032BDCBC487BC1B6BF1BB00C73039C1B9023DA0C159BD505A7EBD9AB842390FB9F43D4C08A83B0C5568BC90D03E3C704F4FBE3E6368BD56EC1C3D633EE5BD297651BBD4E8D13DFDFF31BB6B3A983B6B6B92BD0601783CB2DA773D29D80FBCE60EDEBD3474893D1E3498BB667F533B7581753AF5EA613DFFF8EF3CA3005FBDE9B0673CB833EBBCBC45D93B0C3C4B3D59BFC63CD9F6C13AA178D5BC7D73D3BB9F6FE7BBF6FDD53DBD2C6CBCEE2A853A4970A3BC842647BC5207313B00EF273B896FBABC668C2BBC902B8C3BDF7885BC2EC5DF3C6F89A63C0DF739BC4AB6F93BE56C293C4E1A58BA8607AABCAB2361BC84D386BC01DE10BC4169AF3B70520CBCEAD762BCC819AA3A19390EBC82B56C3C12EB85BC707E0B3C813139BD991D28BCEF65BEBC460070BB987DEB3C41E7D03C582F1FBDE2D1623DC93C5DBD23288F3D9473CF3C038A95BD8C9A92BCA21C5FBAAA289FBD9441B4BD5DF9753D49DA053C4153E63DD92B4EBDF71327BC3E08BEBC40E5EE3BAACCFD3C60EEB4BC432C8C39761FAABC5D86793C808009BE9B3846BCA95C883C2AC7323CA56105BD40976D3CF3700EBBBFAD173D875F4F3D3BAF583BD41E9BBC73BE6BBC1757F53AB0D990BCEEBFF3BDF99092BCE730AEBE203347BC97AB913C0A468CBB7239D03911B06EBCBF091F3C95C4D03B79EDA93C18440DBED217CF3BF4CF9E3BAA5814BE9266A1B9789A5CBE7806143D5FCDA9BCB4A9E9BB646DAD3C5E61C23E37AE003DFAF711BD212C2CBD345CB93B8657D6BD5E2501BD0AE751BE9602FFBC311DC1BD4D876FBD1409BF3CB43C5BBCFC52363C0A10F43C35FC403B7FACD73CD6C801BEAEB1E33B94C1673DA5F181BDED4334BC072B2CBDE8DB7DBD2A099CBC51072E3C7C0E51BD7FBBA23C76EE7CBD9C1902BC3ECA22BD1B0E873C5EF8F83BD6623ABAF41BB93C653A38BB531DEABD4AEBCF3DA3FB65BB935AB03CBC7352BC200C293D118890BDEA584C3C61A57ABDC2443F3BE69D0DBC25AC64BA009A8FBDF1978C3C902A273AADC2DC3C7B21F63BE86F053C5FC0553AD41271BBD44021BEE7A091BC6BC7BEBCB97FC13AB23AE1BBFEC289BB84CB68BD498FDEBD42E4A9BA196A8C3C67A5133CD136C0BA85A7FA3C0DC4473DDA61FEBBDBC3E1BD653A85BDB6AEA9BD8E90CBBD7E47913C8CEFEE3D02C16F3A051A42BD5DFF1EBB3A7B9C3BA960C9BA0E6E163A7FB5E5BB448578BD2AF7313C8B2F0ABC881A0F3DB89299BC95CD6DBCC8EAF43BFDCDEF3AC864F3BC189D9C3C8B3520BDF97BBCBDAC61CB3B0B9256BCF2ECB7BC56A217BBA3B577BD40721E3C524D85BCD4BACBBC5AC3BE3C9B700ABC540DA93CA10EF93BAC049BBD710759BA0A90EDBC3A6A983D8B18353BC577173D7E8D28BCC2A3EABCA8C6E43CA490733C335D8BBC3822B9BC402196BC9C8C25BCCF66B63C94FD2DBBD757223D33F595BB1CBA6EBC5C748CBD6A54C3BCC0C91DBCE12885BBAEB0A33B91AF1FBDC638A83BE3691C3C9D9EE2BC5A9ED5BCEF7B77BA3FCA4E3AA299B9BC97212B3DCF93803C2C89C63BDEF4393DB0A08D3D3A8C62BD07CD203D391825BD5687BE3A5C24D23C650D09BDC19BF3BCE2CD963C5911843CECF716BD1634B13C87591C3C633990BD86F09C3B1BE328BCBEA7B13BD1658C3CA1C7513B215D453A1D00753D9CB0603D8634933BB36FC8BCF1BCD6BC6965EA3AD406CA3BAE87ECBC6C7516B99B0E543D96C082BC53550A3DC22F743DBF951DBCA7F9453C1EC5813C6D9729BC11350B3D36F51D3D1E38A6BCA2F34BBB17819D3A30EE8DBCF0A806BD50966E3C3B091EBC763D703CD735A9BBD4B5713DC137BABCAF829EBC8C85613DD3AFBABCF14C99BDB265B43CFB8BE6BD03CD73BD009C36BC217601BD78E8B3B66D2D99BD11E08DBC86BFFCBC3E68923C9234C33DA68C273D53F5463B1BF68ABC963FA83CF75B183C4E7B45BC82670CBD1E4800BB2A5874BC584CABBC3C5E96BCCA6B283B2EB5183D584FE7BC6B8776BCE91FD0BC83D8B03CB925AF3AABFBDD3B983153BE066795BC89A805394045513A05422CBCC047FEB97263EABA5CF9FA3C1C263F3B60663EBEA0AA2ABBAC36E7BDA0C419BCFF490D3DDC9435BC35B0273C3846093C64C5BF3CCC08833E42EDB8B96B30F73C560246BE287930BC95CE63BEBA519BBCA40069BCEAE910BCF238B23C52FBC63E37B7B83DF61DDEBC904CA93B54C9E23CA0E510BD1DD100BDB248A7BBFE9EC5BB8BAF84BCAA000FBC8F7D9E3CAD2AE8BB9202923D2485FA3A2BF0213D037DCEBC3BF566BC7E5D063C07AC32BC7FDC53BBC04A213DA863B4BC70A254BBA68C953B6813043A5E47D6BB38FDB6BA7FCDC73BDFB9B8BBC927153C05ACCE3C2DDFFCBC7163A4BC8A723ABCA972C8BC761B90BBBAB9783CF81DA83CFF541ABDCDE1F93C8249553D7E812F3D214B3AB9B411D8BD0B159D3CC07790BA1CD8803DE4348F3C93D3133CE5D34C3BEC43D6BDEEB0563D912F2DBD220BEC3A6E1EE2BB06768DBD35F51A3C56EF343C1B9E25BB078CD13C3E8DE33BB7B480BDE8990C3C3C836BBD1E6E0DBC26D5ABBC9BDB15BBAA66CF3D29F1D4BC8E7A3D3B758EEA3C6A5E73BCB2A674BCB4FE003D9EB1E1BCE8EB97BDE46FD93CEB6B04BA850F013C18FE84BDFC6235BDCE0CDD3AB472133DFF0AE7BC59B88F3D8F948A3D530C75BD88192EBD99E67ABC1CAE0DBD9B6B9EBCAEC5EEBB052181BDB204F4B90A6FC5BC4EF3893D93F1C63D78CA85BC3D4D77BC2037643CDF574BBE7B04BE3D146EADBC6F7B3CBD94BC853B999808BD10157FBBFF58353E152D983A1F9F8CBB9C38823C1320D6BD0F8E703DF4FDC7BA7178A83C5CB5DC3CE4E660BBE7A1C53CFA26983D68D502BDDAC5953C57493E3CBB20CCBC658843BDCEE9313DC899A4BC773854BC868AE93B7EE145BC3612C83B5205013C81F4623D040A3BBD56F4B63CD7F628BEF39AA13A5C105C3C84C292BCE1733FB8F46C80BD9B29B7BA444815BBDE72E63CECAAC13DD81C68BD0EA31B3CD02232BD60A8573DAD2B5F3BAE94B53CC1A025BC4A2B523C0B5BA83B986C0BBD3EEFC53C3317C3BC3F4DDD3B1B5822BD0924803BDDD9893B0C11CB3CD7DEACBC6405063CF786C2BDDE8812BD590F8E3BF367B7BD727F92BC6793BBBD2F61003D13E285BC10D9C8BD3880E53B4D1E853C6579C8BBF340D33CE2CBC13D29DDB7BBBA8C783C8A1C213CD2352EBB44DF4D3B70740E3CDAFA86BDBDA4F23B6D5F21BDEE2AC83B6DC2023BAD02E3BC4E2481BD1632053D82DA5EBC6B73053D60A05FBC3FED8A3C850356BDD684AFBC908DAFB8EC7722BCF6092A3A6929243CA623A5BBCE6E1FBD07620ABDF94E42BD5F0EF2BCBD8CCF3CAD24533DF9143C3B76E3973B9BBF07BD9DFE223C769C523CA243643C5257D1BC4764E73BF6D99039CD7026BB08734DBC0C7BDCBB638D3EBCF00B933C968B983CD67E7FBBECF6903BB79F4EBC8EB60B3CB492C3BBBE26EFBC7594343CF0712FBE6EF064BD253C2CBD36B1BABDE58B0BBC0C53E63DB0748FBCBB8A373CD06F05BD388D7E3CE41A02BD2E22D33BA48E30BD2756D43C8D7FB03CF000BFB99F1C97BCCDADA73C195B543CEE34F9BC3827BC3AC91FBBBDB7CD1DBD4CA908BC8BE9243D57E10F3B97091F3D819808BD25BC17BCD2BBC93C7CD6C8BDE0CB553C5646AA3CA8B1043C4E8D41BD3B7B293D2FFE4EBB872724BE0B76D3BBC8DAC6BCA29C813DF2FE4E3DD177B7BC1B0502BC6658043DD202C9BDC9B7A53C5B29193C3AB30FBD2542E7BD54800E3B12FA39BD6B95BCBA4900DD3BE0276CBD5405263A0CA0F23C0CACAEBBFB07403D60CE8DBC4E0B11BC42928DBDB7ABB1BDBBD8B93C65BD77BD9F7603BD4A3221BDF8FD6CBD66C1603D603CB83D39A8293CC1A30B3DC90130BC867D84BB5C0C66BDC5DD4E3CD393A7BCB94E18BD4E5FE93CCC121CBD78AC7A3D2607ADBCD187B03B62BF1DBDC695173D6636FDBD4D6C263B150A0ABD5EE5F7BC40EC40BC366D4F3BE94DAE3C716603BBD2F6DE3C44F1263EDD1E01BD9479E13BF9678DBB6011493CDEBEBDBA4C10E93B0498D0BB136E933BAEC577BCD8E85DBCBD32FFBC5667F33C880115BCCD72903C957BD43BE38CE23C200FF6BDC8A41A3CC358F53BF7CD8EBEAF6C403C686A58BE2804F6BC4F994FBCEEC146BCB577053B889ADA3EA14F1A3E6B8EB8BC114F17BEB7E4763C7CD981BC02EE3FBDC18D59BDD13632BCA4138ABD2BEF6ABDFE6ABEBC6B29193E5A54A3BB28480EBC822DB9BBCB3BB03BA052FDBB856E50BC22B72F3C0467103D0FF59139CF0FEA3CBFAABFBB9778153C6C1FE2B98594FB3CF3C68ABCF80410BCED7E913558C738BD545609BD13AAA7BB252EEBBB13B7A23BA8113EBB5F7C0DBEADC417BD6642633B745DF7BB36D66ABC6E44B8BA6B42ABBB629BBC3C6104133C81D85A3BB02B973B789F013D668E083D64EB74BB3EDE57BC930B03BCB01DF1BBD2E6F8BAC84685BD419EBE3CD19D4BBA773C04BE4A7B223B070415BE508C1F3D085608BC862C93BBA981213B7DDF673E601B543D368B07BC5B7C433D467D81BCE17CC63C940BD8BC070AE63D7B8AB43CCBD1213DC6CB523C50AFBE3A66BDF2BD458F033B963B4C3B6E94DF3B99FB423CD35F09BDB0C7173B5433323C72118CBC52BEDB3BD749CC3A655EC1BC6DF410BE209048BC80AE4BBDCCBAC1BA11B94DBB671D65BD12402ABD8BA5CC3C5CFCB5BCA8F8973C8736013D40A8733B5F8179BAEE1CA8BC18F58B3B2F2350BD01931ABCF6E122BDBACA53BD8740EF3CC66A303E774F8D3C7E8F83BCA6C24C3B03A1F63CAE7AE9BC0FD31DBC638910BBEE5D053D7E0ECB3C4C11BFBCB2DD33BC1315B43C197B57BC6556423D01159EBB9B719BB9E81B62BBF794F3BC7C08C6BCB9922FBCAD35D2BC4F67FCBBBE4D593AFC4781BDFB03A03DA52A533C582D1EBB4A63383C6D15583B822A1EBC14943A3C2295EF3CD87E6A3C80ABF13B3ACD88BC28C2F03A5B0699BA0CE60E3C1A88743C17F786BCCF611C3C2345E5BB01D24FBCBCA24E3C458C40BB8F323CBBEEC8D9BC82CB71BC28F2533BE26FF2BB6E67AABA0459E13C781689BC08BD07BCFE840BB9968C453C4E8AAA3CB030AFBCC2DB0BBCD45E423B49E9E9BBF3E0B7BBA7E7EB3CC9535A3C83E1273DDCB69B3A1349C1BB67D2343C315693BC64C9D0BB154B0FBD68E63C3CEE77F83D4C21DCBBFD470BBB8B62D8BBE88C4DBC995281BBDFE84BB92F1E873CE20D4F3C209CF3BA0957F9BB28631A3C90E24BBC9A585DBB3729543B4A346ABC3F4E0E3C02925B3B7E7787BC33B8B7BC2FC33A3CC3CAF4392166D8BB8D9E95BDFFF6C3BB74090DBCEB96E13C7B53E7BDC0AAE73C6A09C53A0188473DD763343B744B52BBBA56FF3C9D42E0BC8F8FCFBDDB60FC3C48D9FA3C52B6D03A59F67ABBB754A63CED92A8BC7DF4833D5AE1F23CD1154D3C7D694A3BDAA04B3C5DD8A03DD3D6A2BD67EC613CAA9B65BCBEC97BBC2B68ABBCFDFB13BD4D00103D60ED763C87D882BD96A380BB8B439D3D7ABA103EF0D8D8395484BABCF22249BD79DC023BFD74EABD4D57013EA5A885BC8569263C9A97A03CD70A23BCDD1049BD351E31B9C7C1DCBC3C9031BCBA70AEBB800821BDD14DA6BD3DA45EBD1D5F433C8DE09E3DF99A673DB50258BDF94805BC4AB535BB17AAC8BB0F6EC33AFB10DBBB2EE75CBC7944733B01C8DB39B0C7313A943C2B3C9C0AC0BB3279BC3AADFEBABCFB7F743C720E013E1D690F3C89591EBCB70A61BBB4841EBCF27550BC850129BC73FD73BBB400BA3B63BD713BE4714BBBEC70C73CEC719BBCC81532B923E1873CE8C88DBCD1EC063C4882853A384112BEA3F3D0BCC8A6DFBD0B1E6BBD5B17133B37DC9FBCF439C5BB9D348FBC2DD7DC3B5D85CB3DD04A4A3D1C7C7EBA6AAE693D3640DEBC867D59BA4DD7F5BAF1F2953CE6239E3C15F571BCB341013B0479933C483FB5BB7AF8113D65496DBD301B0C3D8D5AC2BB1C8F283C72D92EBC71588B3A8EC2A7BEC121293CCD2EAC3CD345493D2444623CFCDB0BBD3C66C0BD8E98FEBB1A304B3C0612983B92F9863C8142503BC005B63C370A7ABD158B9CBB1998BDBC0AF40ABDDDCE22BDC89DD93B09189BBAB9C8BEBD850938BB8078643AC8BD07B7B1A2D9BB5BBDECBD490D02BEDF3803BD49AF96BDB259C23C7120073D71AD4B3C8A19B3BC185896BD326498BBE72026BC19AD46BC7561D0BAA7FE7ABDB6B18D3C4FA8D63D925242BCDA9076BB1CBA96BB1B02B2BD5800993C366C8ABB8ACF603CAC7CF83C62334B3C783EC9BC070A0B3D6A12CFBD193CFC3B7E569C3CEA35FB3CCD2DFFBCA0982F3CFD6DA2BA6CF1403D7DCD6BBB4C2F88BB87DFC4BCFB16093C0554123D9D076BBC59E23239C5CADCBDB2EB09BDAF1F98BD440B443B2E05F63C1239A3BD204803BC1F3B193C1C7F943D960DE33C53D0DABDF90D073B8905AF3CC0A113BDA903DDBC19926EBCC13817BD9D3B5EBD3F54DB3C1418C0BCA5C0C43C05C943BC64A216BD107B18BCB2E43E3BC33FABBC95E5973C3331D1BC6794C53BD8C727BD7815CE3DB013A5BCFA1F29BE7614CF3C3E73AB3DE7CC3E3DCB65FF3CC3A03DBDDC81013C1B4609BB5E0BDFBD85A6C7BD5781723DEA9A033C2123DFBD8B7C093E101359BC233C35BD924B6C3C02B4843B563A2DBDA416263CAEE191BC9668D6BCC53B263DA39ECC3CBB43CA3C88C39DBC64F942BD15B980BC7C1007BA44A4043E2B65DB3D9B6D1ABDE0D0A13B67CD143C425EAE3C06EB00395B6B86BDF599DC38945E1A3ED7851ABB34BE1C3DA5A695BC1C1CA8BC6F47A1BBF5F21B3C72D84E3BC1E9BF3CF5E63F3E6F4E8B3B113B93B95DD904BE4E8D133D9F0911BE4CE88DBC0C68F53A64A90F3CED592D3CAB20823E13A07E3D9F607DBC423B6D3D0297B1BC68088ABBFFF03C3D9855C1BC97E59EBC5F90C1BB82E1123C1B7E26BD4652B53CA73BDF3B2D0EFCBBC12C7BBC7B3A883D806376BD6D69CC3CB501E33C03000EBDBFE18E3C7A062A3B9EB891BC9C7028BCE4F030BB122220BD790B10BC43A07DBCECD6CABCFFE11EBC83BDCCBC3486D53C825C873CE70518BDBD574EBA1548C13C064A9D3C7AB7963CE16360BE39B3D0BD0120D3BD2CF52FBE7561D53C4014F63DE368DB3BE39C84BC8F638FBD74F0EC3C31159C3D2EA820BC1FC4263DD7C3E3BB151262BBC6FAF33A40B4DABB3A8F4A3D1C02F23B443A2BBD0587803C28E725BDF7532CBD4C5578BD3A13B7BC7A7594BC61F21B3C351C20BCEE61C83CDBF3F7BBDD415BBC5536E63A8D98FABC5ECF83BC52E39DBBBFC089BA3A2F9EBC3C7693BB648253BD0AC68FBCDD643BBBDC2CB0BCEBCB8EBA7BBBB53C75F6923C3C50AABC2D70A23E0BD92DBDE9E4EFBC593DE8BB9CC825BDA8F792BC97210BBDC61275BBCFCD443B5E8B8E3CC531C23B363E08BD847B313CA4BF9ABCFF21013DDFE8EBBC18F8C33C9979DA3A6D40BF3DCE06AF3CA9131B3DC0F210BA9B83133DC29CC3BD4E73C13B905933BC7F9A4C3D029A833A20DDE5BCF465633CD8CE7FBC16744CBD4DC635BC9E22F0BCDD5366BDFA3BBDBDE862A43CB679B5BC3341CA3CF37F7EBC88C608BD13B049BC84E8E53B0738B4BC1A9AABBCF08FA63CBC327F3BACDA4FBCF401273E35390FBD0FF6A8BB6E0BB53A5779ABBB5FC371BC343E72BDF8C3AA3C56E9663E27A99DBA0C684D3D7D94683DFE90A93C93403ABC398A75BC2C9C0C3D3802A7BDD9AE1FBC43860F3DC579783CCCB74BBE38A6FD3AEFFC46BE67C38D3DFA043ABC7E7962BC1C93343B2077A03EE9B6B23D66F1883C06CDA53CF6839F3A23F545BD3670C7BB87EF51BC998816BC3D174EBC4575AD3ADA1B033DB99025BC0284A4BA4C04A0BB67D119BC0129A0BCB66436BCEC8857BB3CD447BCF08F00BCD50BC53D759B4BBB3E9343BC4CFA75BC94C94BBBBCF465BC49FB7A3A398821BC6C98D93B1FB913BCCD783EBC09956E38B696A13CC65A923BCB34FB3BC3BA08BCC796CDBC8DF2253CECA65CBCF98E6E3C6EC599BC2E52903CF74331BC4A65EABDBF8E3CBB0AF14A3CF7FE5F3DCFAA153DDAC02BBD1E6183BA703B20BDEC03F83B39409A3CA4D0A73B4295A2BCAB1B3DBDBF84BB3BF01C3ABC3D4C7E3C0F12413CB252D6BCA23B8DBC6E0D8DBDE5C6BDBB1B43C43C3934843CEC08EEBA4D7ACEBBE5C685BDD4719738E1A31EBD63F6DBBC743126BD0EFEDE3C7140E53C888D70BDAC9062BB50A004BD27BE6C3DC9C3BEBB1990383DDB3472BC239E47BCE325EE3C62D63FBD461E06BCEFC6E4390DE2163C1822263CF034263D3CF0ACBC32CB933CE12C323B1A86C6BC8ADC22BD4E67B3BCC8AE28BD9511043DEF7A503CECFF8EBDE8AFE33D63C315BDB19CE3BB9AD523BCEDFBA4BC9C056CBC8A226ABD9AA7083D407F8C3DB667673B9D3DA6BC539DF839491CA3BBDE35A03C70FA48BB13DB953B50160C3E601733BCDDC704BC87D9F6BC8626A9BC238E7CBBDC088F3CB631C4BC6F6A12BC6A7BD2BB7F0DC2BBA5A038BD37591C3D57B7B3BB720832BCF85CF03B0EB337BCD146633EDCAF86BD3914E5BCC3A871BD26248F3CFEFEA9BCCD1B80BD5392313CBF6EED3B552C943D40FAEF3DA402B3BD9ECE623B91D414BC7AA9BB3C9AA0523CB121C53CB5528FBCE7792B3DA6E0E13C8D43333CBCDF173CBC1710B9F17D3E3C695AEDBCBF760D3D3CB0853CC4974E3C5F0711BC601C22BBDD4A47BE7CF0A8BD8B41563C541BE4BB632DE4B9AE1500BBA67C4E3CC3F0923D9566863C7D5EB03CB2A925BC478B33BCE1920D3B41D216BB225DC63C254CE83C65092ABC6418D13DF3CB9FBCE19E87BCC68C46BB4BD28FBCA2910BBB54EB6D3CB1BFCABCD8DE983BC929013C56E3F7BB9EC127BDED17E53B0A594B3BDC99593CF36726BCC025C5BC5427C63C975C693B18D02DBC31DA4E3B8F2AAE3BC15BE0BC1725D93BD67341BE8C035C3BED21FCBC255E96BDAE7F923CB9FF04BC882DA2BB7DDB243C0BE5223B9DBDC6BDF1703E3C4F8EBCBBF8460DBEBA801FBB82AC40BE39EEDEBC5C53E6BC3AF356BB32938B3CFC8D563E8EA9953DCA534F3C3C60E13C7E6BFB3BC1C46FBDABE316BD7CD1033EFF473E3DEE078A3C67E41F3D89625A3B981B99BDD93E32BC3DD20B3C0AD91BBD6EB078BDE2D19CBC82D5C53AF0CA3DBDD55CA4BC8C6845BC8B0792BCB27A6E3C0B56A7BD5FBF9CBC89F64B3DB4EAECBCCF36033969911DBD105216BE9D6E5ABD366655BB6F9C6CBD85540D3D0E9173BB706FB3BC518A7C3C6F4670BBF0BB97BC2D07EABB904ED9BCE53F96BDD7391CBE9188A63D2AD1213A0F2E85BCC8B98F3E771BA03E658F493BDE0710B806FB31BD9EE6FF3C659CD1BDE3F93A3A467D9A3B52AA1BBBB582343D1A41993B283C013C2E92623CBCAE16BDB6674B3CC6CC303D3C9E7ABEB8514DBD7EAE9BBD54FE683C95CC103A1FF623BD0A8C8ABDFE8DA13DE02ABE3C5CD9CE3C7DA3A43BFF36533DF0BB6ABDC602203C4F4258BC8688B5BD438C8FBDBD6496BB5B2DBCBBFC5F87BD94B0C7BC8848CBBC5D6F18BB947E9CBC98B9C6BC868508BD08F49ABD6CBFDFBB3D2005BDF7CA99BB0895753D1FBB6A3D442303BC5747E2BC5F4383BCFCB956BCA44B603C81CA813D838FB5BCA79A4EBC840718BDAB06243DC74ADFBBF3CB993AEFDFCDBDB513BF3B726C95BBDDF6323DE16703BE108E893D7D3B09BCB10D913C4A620E3AB1ED3E3B9F83DE3BC251E1BC9563C5BD54DD8F3C7C7176BC79F7413C9B4CC4BCC7D9333C2C9C5DBDBB2511BD9A2BE4BB6DD6813CB2EC8ABCD158203C0FD2333D7E179EBDC160013C87D4FD3A54178FBB5D4A523CF6F5313CB5A8A9BE2BCABDBA5FED02BE30BABC3B63CA66BB7D159ABD6F94C03CE5E568BC1836523CD6AFEE3CB97EBDBBAF69CBBD9A1D4D3C3E2E5F3B2DAE21BE1E5885BCC45342BE2FE393BC4433C7BCBBF41BBCB36E173C0A2F853E3512A13DD6835ABCE74895B912F9B73C7D550C3D2E3C983C372E32BC01EEBDBDCD54DDBB9F84BDBDA3DF223CC197113D04AD7D38E7B03FBC5827A4BC543B5EBDF7AA79BC2AA005BC4F45973CA0DDB83BC8882C3CC751CDBADA5A2D3D5BD10ABEF124A4BB836905BDDA13393CD22807BD4CE91F3C8E7A35BDBEB47A3C4C3258BC627C9A3CFC884CBCBF7B413BDC761B3C967ED43A75150ABC25A8F13C00E5EDBCED2303BCB7EC14BDC061D6BDECEA5FBC52F9BEBCF557233BB3AE9ABD51A0F9BC1C58BD3A87FB483CE7AAFB3CB19B1D3BBCAE05BD390D1A3EF0A25D3C0ADBC63C8E93D03B8DF5CBBB05D206BDC174A5BDF00C84BBB5D60ABC314EEF3C94B2003DECA1923DD18C04BD667DA4BC4BED4F3CBEF6BDBE52B7613C299868BDDF1A853CBA8FF4BB9C00923CE6DDF6BC3D10963DB632ED3D0C162FBDCEE7083B4A34203BA2DBBA3CE11E6C3C9A1B37BD80C4A73DCB8ABEBDB028B33D126309BDE73589BC0307B43C70F5773CDEEE9CBDFAD709BA918FCD3C75A9F53B40EB45BD124025BD09C96F3DF22CBF3D39DA163C847BD13C1F2ED6BC1727D33CF7CBDDBBDD5D85391D748B3A841DACBC10BA70BBCF9EA83B295A5CBA657E7D3B779CF3BC5C564DBE8A69CF3B700F03BC12282C3DA676ABBD7AD706BC3E66C2BC13CE1FBCFBDD84BEC3EBACBC0B63D93D1F3F5B3D9B79F5BB5E0101BC22A61A3D0C4DF83C39AC09BC88928D3C1EB7833CDA5ABD3CB8CF40BC6EDB753CBE94F7BB61349EBAD9D70B3D05BAC2BCFDCFB4BB8533A73CC452E3BCCE8A9BBCFA7727BD7CF6E9BC58D20E3C3421103D87C926BCCF7CBEBB8FAEA43BF17E92BCCFB38D3DB76AC5BCD56D193ED13B783C885266BE198F76BD2C691D3CE8C82A3DEA6932BB2CB7C8BAE0D1A73CA13F1F3C32B446BC41D2E9BB919E62BA978E26BC9AD2A1BC794CB0BABD014538121AC1BAED8485BB0541D0BCAED816390EA1E93B1CBC7C3DAC35C5BA1693EABCAEEB26BCC773133D4823693C4A24343D505461BCD48F16BC2BACC43CAF699ABC1D22EBBC8013973C44DB0BBD88218D3BD8C39DBBBEA2A33C553147BB1ED308BD9332253C3E14133A7851003C2604F3BCFDD79EBA544C3BBCB3C74F3DCBC7053B7D7B533D936B27BC853246BD5FEBF73CA17E383D09A20CBD928023BCD51C873C3634A6BDF055EFBA6EA9F5BCE1B8863DCF7682BD8807933CA41805BD82780F3C36D7C9BCEA81133C08CACCBC488653BE63DFFBBD029183BCC9A505BE5C7C33BEF8AD11BD4A464FBD077C0E3CA94CDC3B71EC9A3B77B21CBCFD4182BC3CCFBB3CA79F4CBEDC4B81BA34E4763CF5D19D3DCFCEDCB9642653BDA0086C3C8714B03B4A73423DA8AA983D738458BD508113BEEF7910BDE9B70F3ECBE984BDF954D53AE2245ABBCDF8BE3C657C80BC79C7A03DC30B5E3D649D973D4DB9E8BC05F54B3DA73C8ABCB949E5BDB8B4A13DE332063D01F203BE3DBB2C3D6E8F193C6A5C5EBCD205093B3E2DE6BC94A1CEBBEFDE2CBCB8FC0EBC7848703C9C9C3BBCC0C6793D2A9DB8BAC42CFBB90C7905BC7FEBE83C7531043A2C7451BBA32AA3BC0B2B273DD11147BCEBDA073EE53138BC93F1BA3CD4A44ABCC9D02A3C6CF207BD812475398FBB3E3BE434303BC2CD0F3C5A4FFABC2A7099BC2437E43AB7CCD1BBD5ADD83CD0F87B3C096E7D3A920DB13CA1F62F3C68A433BDFD02B5BD8E3911BE550809BC299010BD383126BD7F9108BD026B933D5FF4DA3CF62F0F3DB40B953BDC8D803CA4158A3C878619BCF7C55FBDF2538F3C5B3801BD4A9FA1BD3F0BD9BD88E33CBE569840BC514FF8BC8443953CF1E2B5BA76E30ABDA10E40BC31CB6DBAF06C22BB32C76B3C889AA2BB034880BCAE010DBC8F36943C8EBD773CDAB60DBCC46FA83D860F093CF4B929BB00BFDC3CCF43BDBCDF5A75BC593CC0BB9DB74CBBE613CABB58F2CCBC9E44513CE685AABB628B033BA7CC47BB4391B83CF1391FBCBD4ED33BA664AB3BD723C7BB5836993B368AF9BB3D1083BBA805E1BB38ACD7BC846331BD9888C8BBBD3D60BC756149BCD465B23D7EEAE63D47926ABC24A1AB3DD814F63BE3EB3D3D990C1BBE18F6333DC31DC33DE53085BD2337023EF00AA0BA2E12833ABBF737BC47ACE33B60D3953DAD3B8F3CD4D52EBCE6430D3E9770E53DA69A373DA66B033D96AED03DFA71673D89D515BC9948D53C20F7883D47382ABCE6549A3CF89DD2BA1F76B3BC592B5BBC4B8FC6BC651922BB110F56BC5E8E10BC70C68F3DB92AAC3CBBC8D33B1CCB37BC42C828BD9FFBEBBB7044973C752D7B3C555EE3BDF1D9E6BCEA85EEBD3876B8BCC2AE52BB2DC7803CB5ECE33C62773CBDBF48D7BB771D273D46CA2A3C8F30AF3CA5E1B5BCF7A701BDC224873BDBFA4CBD36E82A3DF86EA43C3BE96ABD5A2314BDCEBBF63C33F558BD6AE27ABCA76BDABC4F1C09BD0C78EEBDF2646B3D3AA3E7394E2F2D3DF1B91DBD416964BD1ECFA13C0895F3BC90D1013DEF4D703CF85D22BD40AA0BBDD0F9D1BD4A891A3DA73631BDBE5631BDD2400B3BE49BDCBB3971CDBD58AD8BBAB7A6913B118716BCC13BBB3BFF8952BE72CB96BD14E5FFB90370863A46CA1ABDC3940C3D8A72AA3D38F80B3D1E332CBCA7477FBCEEBBBEBC0BE48C3D50F9C2BDA524FC3C66D984BCD410AFBB6FAF4ABC5BB29C3CED8E163C5B8427BDA7FE44BE198C8DBD46D654BD69813ABDB54C4E3D834AC73BD57552BE52D6363D2BC9AC3CE6991EBD553EEDBCC87EE0BB375DA4BA1E6E73BC065E173C1740263D614D323D12D7CB3CF8AF88BD515307BDFF56933C0EC823BE54B131BD866D8F3B9F9EB23DFA55D8BCBA9448BCB34ED63B2CD2E6BA7AD2AA3A981D563CEEBF4CBDDE1D80BDDC6A28BE341C6FBC42EDCEBD3B0F10BE1A935BBDFCE3B0BDBEE2E83C229E58BAAF9C533BD71AD83C9BD895BD6CCD58BDF69A7DBD312C19BCF297CD3CD41D803CCDA53FBDF79E02BD9396C2BB4D74233B06A44D3D4ACDC8BCF86A0F3D70CE5A3DA4C00FBC7B77183D3728A43DB35EA5BCCD16983C4589913C826B6A3D8FFDCB3B16A4593D1F635CBCF0D6F93C0D2625BC5FDF753DEB5F34BD3A5D16BD2A412BBDC5B94D3C6AF88DBD43E987BC0551FD3A6573CC3C730EFCBB2B3391BD200F6DBC6A0ACE3CE16354BC4E3E57BC6812E8BA23DAAB3B95E50A3D0B550C3D926153BC4D63DCBCB514A43C0B4826BC9FEEC7BB788B0ABD64A1643DE6DBE0BCD051173C36B47EBDF644573C042A97BD70BCB3BCDA92EEBCE435B43969BD6ABCD96DDE3B781604BDCE3FE3BC528DB73AD9F152BC76FE8C3C75A279BC8C0AA9BC505383BC85301C3C42DA823CCBEEDDBBE538EABB24B3DC3CB893AD3C1274E13CBC4FD8BC14CF66BC178D013D440C8F3BB27697381F3E5BBC2371183D60498FBA5E9C8E3C3E8FFB3B2A682D3D35CC95BCC7BCB2BBD693B5BA095919BB5EBB2D3CD2F8173BF1201DBCDFCE59BC769D09BD70297A3C13BB833C0689633C5007ABBB543550BC2A3D83BE111E303EC8F15D3D80DD193C35734CBD8F514C3EDDE526BD9F66C5BD6925763C9E9D0DBD2A381EBC28B6ACBB738FAEBBB7F1273B189D87BDA3FDF83BB9E858BDA4B487BD4B9E113CE3C912BDCEAA1D3DF91E4FBCD55F883CA63582BC2027113C8E46F8BCE94FD3BC01C6BBBBAB57693CDB960CBCDAF4F43B00E6613CA5B3843C792F4DBCF9698CBC303037BBBFD9DDBCB4ED8DBCC30C113E5DDD80BD70F0D4BAFAD9B3BC6A031D3D15E2013EDDD971BC0077C8BE7007A23C82234F3BBB1DDDBC2CCA203D8FA29B3D0FD78ABC6883123DD971533CD5FA803B96C5613C4A0E4BBCAF5052BCD511B23A44DBD4B98020343C99FC2D3BA832A0BCC875BFBC436BB33BE15F2F3CDB76873B43AE963BD1C4883CC00D013C138C883B127B63BC4A01AABB5F08863CCA995ABC86BB013C911A80BCABCBA3BBB7DFF9BC17998B3C950D173B7D743EBC46B90BBBF714C73C05249D3C7F5D4CBBE2ED8EBC317493BC356766BC84C08F3B7540CFBDCCC5EA3A5114493D1DF9673BF0E21F3C681C3CBEDA8EBEBD52F2FDBDD8DC36BE7190CC3BDEE5213D7CB7A8BD65BE35BD6264003D875D243DF83B373E898603BD25BD6CBC95819FBCD1442E3CF5EDE6BBB13B523D309DEC3D548A013D4D20983DA5479E3DA4A7203DA1356D3D541B30BDBB33D03CB78E90BC985935BCC8AE23BD393D3EBC567F71BC97C7C43B987FAFBC7167EBBCFEB3213C61A5E7BC30EBEFBCFDBAAC3C8BF92D3D443BD9BB3441883D68F97FBC185758BCC8C39EBC544310BDC0D6CC3C4781DABC9DAFA4BCD0201C3DF50D153D54078ABDF29C513CF410DF3D0038063DC61CDBBC84F715BC28E5873D857BBFBCB7D4ADBC286E3EBDC4E7D63C0C5CD5BD6BF049BD5ABA17BD3A75073D3F330DBCD719413D5C540B3DADC9233BF8194D3AEAFAACBD3A3F05BEDF70DA3D3256CEBC9D89FB3B6446543D5C4C7BBDA9D6E73965DCBA3C417FEB3ABBC34ABD236C55BD0BBB36BD77AEC83C437723BDAA445F3D1854AF3DAF03ABBDEB2069BCECE1333C6D6E13BC0E64173BE6DBD1BCA4350D3D27F5AB3A4DF0AEBCA8619EBCCAC7103D2B748ABE88C80C3E15BD24BDBD8BE83BFC8023BD17942E3ED39F3BBDCA0BCCBD5395363CF63865BC1A033BBD0EE976BB02E6003C5E0DB3BBF91544BD215130BCEC7E88BB02B4DFBD9FED1D3C7974C1BCE719443D074990B941AA973C29A116BC562E403BC794C7BCA7AFC6BD2F02563C4C1889BDE294003D567DC3BBB428D4BC17D727BD88EE89BDCDB9A1BD361850BC241420BD3E5D05BDB3A577BD1C7572BBA60FC8BC877F9DBC20E861BD7762883B75392B3D10F7F93B6591BBBC80C88C3D2E8A683DF050573DEF4D9F3D7DA123BC4AD9173D1E458D3D0BFCB0BD939331BBB8E184BA1ADA553CC000E9BCA032AC3C53CB4A3BCF8DEDBB1FDBAA3CCF139D3B00EF0E3C4DBC123D86B4F2BCBBA05BBBB254AE3B405AF93B96A482BB03EB003DEB871ABB9153E23C2770A0BBB34AA4BAD5E58FBB7D6C043C6B3DC1B9B867893B0B0CE8BB1F4E883C223D303C203BB7BCD137793CC17B5FBCA55D45BC526C70BBE6E63ABAB2862D3CAC7046BDA7D8063DCA72C33C94202DBD2C364CBD99FF133D606849BB8B5CABBD9914B8BDE38D213DB0E03CBE47ED8DBDF6C0F1BB91A42D3DE50CC2BCF3EC8EBCFA7F80BC5D60823C081DC23C86A0993B51DCA43C08E086BD7BF923BEFB2BA63C84F86CBD22CA2DBEE571EDBDDDB5C4BD9DB0D9BDD314373D5E7B093DD50D153C5595C7BDE5BF76BC588426BE7855633AC126893CA37ED0BC9354653C237B9BBD8B1783BD003DB13C64A4D33CE72E4C3D96361BBB6C34DE3D8BA090BD687F8BBC0BC2973C13CFC73B2FF4693CFFBD3E3B7DA9883CFCEA513D5A6B673DE2D5683D18FD9A3DB9CA813D5D1BCCBCD6C465BC39C605BDC2AAD1BC9D0123BCF30F703B8FCDF83C402CBFBD9678B2BC74F896BDFAFC1DBD1EE7453DFAAE2D3DD31AD83B5A1EF3BBF05361BD26D42ABE2743253D421DA5BB99E4C9BD15BF1BBCCEAB69BDA75387BDCBAD01BD7687E1BC68163A3C57EC8E3C2CBEC1BD155309BEC1E60B3C9D358EBDF84930BE4CFFAEBDC8D617BE9145AFBDF13CFB3C1568EFB91AAFB93C55DD9ABCBA1F3F3B3B77A93B64CDEDBC69D068BC1363663C0FAC453B6E46883CBF3942BB6B81053C6AB14F3CE01115BBA2DAE13CCE1883BD2C34D5BC73AA4BBD9A0A123AECF7B7BDA5139DBB6272343E9907613D2AB9C23BA73B3E3C6308E13CB81FB63BC5066ABCAE23DB3C97D5C53C58DF813B59CE98BC630EB13B6AAF723A12E827BDBEEE0DBD2B69473CB1D244BCB0A0783C8200EBBC0BF6ACBDC0A918BBF65E873CB7A9B83CE87808BEF690E7BC3A18583DC1DEAC3CC3B5E9BB94BBD83DFC879D3CE6B6CE3CDFDF8F39DF2754398074D5BC595B183D55BBAEBDF6F493BCD53F71BD4618B9BB4B9620BD6F7412BD267CE83C0E32FA3B60CAA33B041DC03C3F2A2A3CBB4AD93C802905BC7A2689BC443A033BD0E0DBBCAB776CBCEC696FBB9FD3143A11A0E53D69A7583DF2CF533C6F47C2BC6B8FFFBC2264FFBB1244413C18191FBC4E4D103CEC911FBD3F29BE3D8FDB5DBDCABCDABBDCD7DA3C9BC5B33B542E05BD7A2CAD3BDF3939BCAF2F4A3CB60D0FBB6166643B2EBFBA3BD79F2EBB9A108ABD0B8078BD9D05F2BDC255343D67B6393CFD7B49BDEC4D3EBC4564A7BCD324F9BCA3C5763BCAB0F0BB418497BC303DA2BC726A983D1AF08C3A7212B0BB4756953DF8BA41BCF563963CBA0BD33BC6C8D33CFB7280BDBB5BDCBC8C11333D4C58883C0947083E342E11BDEAA540BE1B1927BDBB564B3B97822E3B4FF2C2BC81748DBC4C27F4BBE5D6213D500D23BD7C1F003D09BA70BC30AEDBBDD6CF4E3C113E663D61AF89BDEDE806BEAD7083BCF780813D82B19CBC29957C380E85993DF4CB4D3D76AF90BB9AF584BC208CA33B645821BD2185E33C171AB6BDC439FB3BDC5A8EBD1B7BE6BB0FDDE4BCC689C2BC0F6F0E3C1D5B05BD517EBB3C70040E3D98AC3FBB223580BBCD9793BA374F0BBBD26E84BA062B293C9900163CD8D900BB74B7D6BCC17928BD3D021CBC089CDB3C1999DA3CC66C533C832A893A2649A1BBB3FA3E3D5C1D90BCDAF617BCA17A55BCE3A818BD5C493DBB115ECBBC2812B5B9D3061FBD5EDC14BC5DB1823B49B72BBA434B473C1301C1BCEDCBE4BC3FB4BCB9698981BA459FBD3BBD7CE8BBDE389C3B542D0E3B157F47BC0558CF3BCD6A233CD766D03BB0938C3CF972583CA4DD64BCC51E4EBB8046A03BD21AC03ACA6704BA2C048F3CAD5A503D74BA2BBB5EDA263CA198CFBC252B973BBE822A3C5122A7BC7BBE14BDF9ACF13B53413CBC603736BC12779ABCE4E04EBBE1421A3A99ABC8BB924767BDC2A8E6BDC09DB4BDD8CDA7BCD2B9CABA2B6C513C8E50133DB17ACF3C2ADB2C3C08A17EBC249E933DA66EB5BD4E70B63DE48A3F3DF56417BBD4E80A3CDA749D3D0734563C1E15073C307AE5BC6458593D6E0126BDD6D9D1BB65F1F73C9B0DBE3C4E1ACD3D1ABC7F3DBC3DDBBDBCAA5F3C0141C4BB372F573C539895BDFDB830BD19552F3D2DE2103D72ABACBCBBC21ABD6489E4BCA790113D324487BDCE28B6BC4EEB243DEC2F6ABA4ECF0ABD3F00893C7CAF99BD29D88BBBB2719ABB0119D2BA21AF2D3E7110DFBC274096BE632F29BCE670A73B2AC5A83D1F4C223D5D2946BD932499BC64D24EBC1B05FE3B550F59BB8D7BB73C1D4458BD427C0CBAB06074BBD9C8033A83A3363C2FA01ABB0CF432BB8A3428BCF1DEADBB5E5F543C3B749F3BDEB2AC3CD599093CFB3845BAA300D339514483B973AC14BB6B5DBE3B9524CEBBD16E663B745EA4BC1AE029BC1B5D18BCFED6053CA8A7053B64A271BC0C20423B1A63D43BEE5745BC436542BC386C02BB74AFEABA7F1D24BB1F480ABBE2E9CCBC1B7A0F3D605B503DF71153BB668E123D5312D13C63B317BD5F971B3E2C3F923CFC9C0CBC817E39BDBF79463ECA4A11BD8DFA81BD76D51BBD510926BD23990E3DF8913EBC59C138BCFD5E0A3CFB3B9FBDF1D3173D640251BDAA22063D5B445CBC0F8F3F3CBE20573D0BFBC7BBF5A0213C1EA2303C9946BD39FF1E84BB02F759BB7D770D3B52E7833D3F26E5BBB90092BD18063BBE2302903C53C49CBDFF3B3ABCEBD6A6BCAB9DD63C6CD6013E1F524BBDB0CF2CBECD0BF73A90D4073D3C3E5DBD220F88B8CEEFDBBCB2CAAC3CDDEF86BD109144BE21F00DBB56D03A3A6C4AA0BDCD5529BD30DDD3BC2AC833BEC3E61B3E1579BDBBC4242B3B4DF06E3C3275CE3C3B9E22BEF68024BE7B5F1ABDB0AAAABBFDA506BDA886103D6E0C8C3D2F9EA03CCE8D7BB85ECEDBBD12644EBD32DEAA3D38DEBBBDB7CA513C6DD7BCBB17C561BD67F89F3A3BB0A73C5E1A543C8335C0BD901CF1BC1E5894BDF8E92F3C1D56A1BD17478DBB5219883D95BF21BE75FDE9BC13CC4FBB8863FD3B35E57CBAED18DABC8798B339F62048BD561766BDC90146BC139661BCEFA551BDBD6B8ABDA42D60BD317836BD539721BC8CB47B3D2C54C33C3DE6D53C2B5184BCCB6DE4BD412332BDB18B8EBC6663513CCF1904BCA5571ABC9AAEB43D68378F3D6D993CBD0E08693D8320ACBD7829443D3A3A1B3D45C9EC3C13E3ABBD36CA77BB14CE3ABD5398643C3311A9BC48AF333D1378E2BC346E263D7A3E6ABD15F94DBB4BD884BD16AD39BE28870FBDBE801EBB5EDAAABD2932483D82E1A63DB710493DA4735F3DDBF8433C67E21EBC7845853CA1D39CBBA5B2243DDC682A3DD94A0DBC0D8B37BD932D513DAF8363BC4EE9883D2DF6AE3D47E17ABC3C8BEF3C6989ADBA339C193B3DC28C3AE2BC1CBBEA318BBB619820BD3BE9A2BC16D0B0BC5A677A383E69AFBA9E6EBB3C4112AABDD3CFAA3B5F2B18BC2BE77FBC5765C23CC859203C6722BFBC0BBB63BC4C2CE63C4479C73C5E55B9BE4CA008BC0C8D30BBF69C813B250867BC03A57EBCFA54623C13A189BC71F70EBC741660BC8DF0E2BC5FD9CEBC1F7663BB0CE886BCED3CDF3BDC94D139D785ECBC94922EBD4A0F4DBB2575113D7FA9B93B3317E9BBA8E8F3BC2142383CB436DDBB3F7B533D8EB816BD10B420BC48FD0DBDE26D1BBBA4153C3CA6B9C9BC83F353BBB00094BC8D44F8BBDA5564BD0CA928BC733988BDC78BB8BCE068A6BC4DED01BC3A912CBC66892EBD3E8727BD1C80E6BC887BAFBC1BEED03CBAFA3A3DC304013DF88D823C496CC93C5246593B190ADFBDFA7781BD9FC47CBC25D5BEBDF4ECB6BC681430BDFD390D3E691002BDEF90C0BDDFF2243D20ED36BAB6FB053CC1113BBCC97FBEBD8B01BBBD80D316BEA5A846BD3EBBC9BDD32A85BE439F42BDABD296BD88135B3D9AB4E0BBDD0C973C08871D3CDA39EFBBD6A14D3CF3538ABC28555ABC31346FBC1B9E63BD9B725C3CDCF43EBCA9D64ABB40428BBCF3B6D93A5CCC683CF23C893C9BE5F1BC297F303C011A25BA7E6124BD3FAC71BD430C7EBC13BE393DAF79DDBCC7C34ABC7116BCBCEF033E3D751DA63CD052A83C54CE00BC45A911BDD53DF4BCA0031CBCCCB7E8BCA19381BB0EC4B6BC88B560BE668BADBDDA569BBDEA989E3CF14BC0BC3C3742BCCF300DBDEBE8D83A100F58BDF7EB503DCF150E3D217A433CA761323DF3E88CBB31CB0CBE2F30C33C5AB081BC8FD1A3BCE38429BA16E0D5BC8CD1893B62EC133C70890CBDBDD1163C9F06B33D3DEF6FBD79CEF7BDCB8D70BC943A53BDAE5120BD90D99DBC1459053C077ED83CD3F1EF3B8369463B32D1FF3B9B8490BCCA6A4C3C4DBB573C244315BC95444CBE6E47B53C266AAA3CCE327BBD2A1165BDEC85943B2446313C4089B4BBD034D23B765094BBFB2389BD8904BDBC85629B3AB4FF1C3C4C7D2F3C402B3FBD1C36473C9975943B006C773C3E4C48BC4126543C8CC4D23AF1BB153C5BB86EBC167F42BC439925BAFCDA1CBBDDF733BC70C9E9BCE709A73BCCC4B23CBB8E14BC0B59093C06D7E7BB7A040CBC02C6293D201300BDF240A4BB2F4F71BC5ED59BBC425C693C595BBEB91554CABB6DEC153C3808B6BAD7CC51BC50581EBCFCD5BB3C0BBF9D3BA756403AA5F6A0BCF630343C5F3808BCDE74143B26FF2FBC0E3D36BDB4440CBDD4B588BD608F853BA0E69E3CEC05193CA4EDC1BDF1E4DCBD10018FBDD24C763CFD01FCBC1A35653C146069BCAA7FEA3D5F5E823BD86F77BD601C173DEA8FBEB9A2E0443CA739553CB479233C25C36F3D1613633D87932DBE38D3363DC87852BDFB24E4BC404E25BDE02D5DBC61BE59BD0848BDBC1687BABDB59218BD4B051FBD66D1DBBCACCBC6BC9CCDFCBB13A5DBBBCBE562BD68270ABD21A620BBCFF1873C0EC6A53DBD3AFBBC59219A3D5C76903C589C53BCA992853D5EFBA43D0F4B023B3753B13B0C80703B988C9F3DD41FD8BA6095CE3D36DF3F3D81EE2F3D05C2E9B85EC4453D01DA94BC1DD3A93BA8550ABCACC3633CBE522EBD646682BBEA66B4BA0B266D3BD11685BC0F7E0FBD505BE2BCA93ED53C5BDF80BC7A1434BBBB96193A23989DBC22B603BB895A4E3D62C6C1BCA18695BCAA133FBC32E0643C59FFCDBCA73B613CF357253CBEDE07BD5B8C18BC8B6CC8BC37A77DBC5419EABCAEEDD33C57BCEABCF2A8A9BB6CAAB6BC6E6B88BC250AC2BC70EE25BC079FF3BC57451D3D6B436C3D429E74BC98BA69BDF2C70F3B7AF398BDDA6E1C3E6D05FA3C181C00BCD3BF7A3DD815433E4983EBBB771A9FBDD458413C171C8D3D6D5B0F3AAF73D03C18F509BDB5CB28BC78DC753C38BFFE3CDC7D893D5E9C783D6DEE8A3DA3692E3D61E6A03D9FDA76BD2273A33DF421093BDD246DBB657823BDB38F37BB3E0C103B8D75303CE22D2DBC770D00BD29FD0A3AABE7703B034AEC3C95A5B93B107A653C251987BC7DBF863B3C3C3D3DC42B8EBC38EE6CBB63180ABDA73D68BBD4395B3D34841BBCB28852BD54F6763BF8826A3B3D9EA5BCC514A53CB2FE183DCB8C793C59F11ABC5FE8B9BB47DF003C820D4CBC18BD89BCD1F0BFBB959BC0BB68259A3AA41E3EBC56CF92BC2B0343BC2505C6BBA54B5EBB34480D3C1DD288BCBD1F443C5CFBB4BCE3D2BEBC6D97D33CA9ADB2BC8F4F89BCB11F36BC01311E3C3470F83CC95C113D878634BDF7B5A93CE85013BB67302ABB6FFE66BB3576AA3C3DFCE43B91C188BC762C8BB9684C15BC4EB891BC7D0BE5BCF6262ABC9896703CEB4012BE6037053E326F01BDF17D95BC8F041ABEFA96CA3AC6E3C2BC9C20A93C3937393CA596903C5D86053D9457A33BE0ACC3BD702C993C6F35763D94E997BB4401A33BBC42A1BD34C8E7B9D92FF93C81EDB1BDF1E9D1BC6CB31CBDF76FAE3D6A25963D17DE56BDA24E30BDB252BF3DE9009BBC6259153D957B98BC37113BBD08BB1F3E49E7363D95A013BD026696BD4EF3A8BD0B859C3C44BF24BED4B72ABDDC381A3BB146CD3C7176393CBB7F8ABE4C3BA6BD78EA91BB08EDA13D54DB7FBDB0711ABD11FA0CBDA39AC83CE510F6BDA85FED3C48AC273E1B8A3E3DABB476BECE29443CB966973C4A58F63CEFB6CBBA79953B3D0665A5BD32D38F3B07470A3D981A05BE47FF36BEF9230EBD016E8A3B9E3D0A3D37490D3D8398D739DB25233DE80750BCD3EF143DAC0EC83D35B94B3D8BAC20BE0B444B3C47912DBC539076B9D5A235BC9021A7BB1299763CEEBE0A3D0924133DFE82AEBD5CB4E83C36D70D3B60A76ABC1C4290BDB93909BEC8C7193DDFBF43BAAEB9E3BD9C8D3F3D0E5399BC270022BEF5C554BD0A60F4BC958173BCB5F00D3D6024133D6DF49A3D737D5B3C3117063DDD58DF3C567334BD537CCF3D9B2C37BDD395D33C20DDC83B17F3683CB66D353C7CB8C53CD1642C3C11BADDBB47ECF6BDD180BFBCB1F17CBD6D16D6BB1717C43D38B5773D409714BEA3E5A53A652E6FBC5DBAD4BCE1294EBD474CD63BF8019B3CA0A35A3CD4E8FBBC18DD44BCDE04A03B9940D8BB14E77BBC2047BABA5C13A63C62AAFC3C9320A2BC873DF2BC64AE89BC99F955BC10BA45BDC7F91FBCCB6653BF70F0EDBBE2653F3D2B27AEBB3FD81CBCEBEF95BBBE5AD63CAE2598BC257A613BBB95DE3BEF02D73C12C628BB3A2743BD3B513CBCC8EA313C54E77F3D6B59EDBD198722BE0903F23C5C3A24BCD09F013C63CB023D0C21CF3D69F77D3C7C1603BC441B4DBDF629E6BCDD14813D534ACFBD631AFE3BBAB2A2BB61B396BD6F05CEBBC3C139BCD43F16BC875A34BDCC96C2BD0366B9BD0CBEBFBC1CD3A6BDBA377CBD35EA6C3B3AE268BE42F7833D0C74B43B09CFBB3BCE8500BB8E8E6A3C6F4C00BD6804463BE0D656BDB9D51FBD0115033D0534323C3914F33CDCB2B23B6B24C53DB431FDBC2643983DAB31473C522DA4BD3DA3AE3B16924DBDE864953C114D623D9078C1BB84512BBDCB688DBCE7A502BD703266BD91257D3DEEDF11BD970F9CBDF20DBF3DD150003D055B363DD3B6B33BE742733CDB6A253CF4845DBD9423BD3C0B66C43B5DD2B7BA4814593CF563D53C3A0FDEBCB94F8A3B8331AEBD40C6ACBD7A6B4E3D998F48BD4D22D2BC4E50B4BB8391F3BCAA5F14BD888846BE66A598B9801B0E3B75CB123CC45A673CB81B0EBE277031BB5EF1223D1FDD6CBC7FD88D3B39DF393D6F05173D1378A4BBD3F860BDDC6A013D173AD13CCEE9B03C5D8470BB65C9FFBC1E0CAEBEAE5660BCF15C6F3B29BB4BBC92F5D53CCE3CDF3C248ACCBB0AE634B94E9407BD45BE5A3C7A8933BC993449B86719C03C4E1C323AF81D243EE243E2BC3D22D4BC525F89BCF38706BDFBFB743DA3DD02BB118D693C632B1FBE1ACEF3BE77C6F0BB45D4873D567512BCA9D8EABB9A9402BC903B5FBC21521EBBEABC4F3CFBA59CBCA37B293CDD48543C4E6A1C3B71B288BD4AB0F3BC8D87AC3C5B368E3CBC50D1BC869E80BC9BE4943B00B99A3CB33A42BC57FD153B734179BB7871A2BC217C343CAA4CDFBC3E52243B9FA231BE4459AB3C736A71BCAD1D143EF5A47DBE169BCE3CB26FB2B97F8C80BCF24CB1BC0CEE61BC688F2CBC1E55523BD0FA64BCDF59F2BBD643673C01FF4139CD04F9BC08482E3B5B7A053C24E3D7BA59BF383C709FA83C3754143DC7BD553C2A8DB63BC276DB3A3BA7BE3C8BBEA4BC6077983C3A1309BC6468A0BC25D367BD6E5BECBAF8849E3C92D46B3BB6A1F9BCC77DCFBC2F583FBC1882AE3CA98D06BDB7D439BB86F2373B49CD25BEFE3DADBD948C173C3884C8BBA755AF3C90E8E1BD34A1F6BDCFF1663D35471DBDD963B8BCBEB0063DB8C274BC0A0D0E3EFE9E363CA54B9B3D87BCCABC6A5A1DBE8EB0D83B281E97BD06C08C3CCD807EBCF0CFEE3C69E5373C2CA032BDE13CD3BBB665A63B3FB3A13B4E1F163BB66FEBBCC02BE1BD11E5D3BB7CC2B1BC08A98D3DCC100B3CD2E9F43B45894B3AA0F516BEE01F4CBC57A22F3D74972E3BEA9765BD864AFBBBAFD650BE0BE3003D87A420BD88CC2DBC32FEA2BCA336BCBC0C7E8C3ACF01C83CAE0036BD9DF41E3C0E165D3D9C4ED53D8F20ACBC5A151CBB2B7CB03B2A0C983B81C8BA3C29884D3CCC868A3B00BCFE3CDEB9C53B7E83D6BC355CCC3BDC286ABC73262DBCB0F0033ED085973B9A750D3C6478C8BB5BE6AEBC5CF83EBC961718BA79E56A3B79FC51BD367F44BCE71A92BBFE83AC3BB77B01BCC68541BC80DEF2BD99E7ED3C5FFF19BCB7BF98395883E13B29D888BDC1B1063CDD2EDBBD8F0AABBCDF27143D9F28253C2A66C83C27DDC23BCF8CC3BCB1CFEABCA80A42BE4B60293C27FC3EBB915DC6BCEC21ACBD6C7085BD3E97ACBD328BF23C3EF488BDC8997E3D37522ABED535DE3A91E3043EE27E833CFA95803D8E8673BD617E893DA6054C3CCFC9E33DFBD5963C680402BC979CA0BC154D4DBAF1C4E7BD5CA32BBC21B6A63B31580A3D5C125E3CC8F6C7BB9A46DEBDC45312BD198DD7BB35E1103CC3FE46BCA47FDD3B5098133D024E793CEEC2D4BAE4438FBC0E5224BC66350E3CF8CB05BC8E1CC13CF6AE19BD9E1335BBC4F7DBBC588194BBFDF9B5BB077E7C3C049EAE3C461BD03C198C023EC1B2A33A2E76F6BC57BAB2BC7FD6B43BFEC0943D4B3D0DBDA5F338BD5B1B09BC772727BD887B803CEDD6C9BB0647A1BBE304D63D4E84AFBC7563A9BB74AE43BCEFA3E33C5F96833DCDA7903DC0339EB91EF5653CD6A4A0BD5239BE3D1E1D843C332147BDDB9FF83CE310DA3C446AD1BC1BD40DBE826445BC2EC286BDA5252DBCAB932F3D6A33E2BDCA4D203C5EE9A03D4B66B83B1EEB963CCAB167BDCB86F1BD197DC0BCE691353CF8E4233DFF16193D3DDB9B3CB7D0973C2B486CBD0941FF3DA84C83BB35BFC4BC940CD4BC1B20EDBC537000BC539ED1BB5F9535BC50C80ABD9BFE433D9027D9BB0C9C353C3F4354BC3DC7BFBBE1CCDABEEB472F3B08E4583B533144BC352431BD20749BBE75B8A23C8ADEEDBD007669BE7A80433EB336C03C1A7A873D6603D9BB4B883CBB541A96BBBAA5D1BD08A8FEBDE16F11BBBB3D233ACB7C17BC1DFF20BD1EA5DFBD9A2FC83C179B67BD4C32863C595631BD71E8AC3B3A34E7BD3F43BE39C51923BD2146A8BD223817BE6A9B44BD6C1F12BD8601A93C21A1CB3DC924F2BC1090F63B089015BDE78E6F3BF36D143B7EC38FBC2E0A29BE116018BDBD4380BD7CC433BDBAE6C63CB77680BD3B4D30BD30EC82BCA40EE3BA88D61CBCD4FFBCBD8EA3B43BCC33EF3BA1273A3C7D7D55BDBE9C89BD8DD585BD5CF9DDBCE874943DCE8638BDF112153D172E1B3D94658BBC1592BAB9530ECBB965826DBB8BF83D3DFF37CA3B290FA5BCE43D923BA2807EBBE944213C1EFC193D791992BC3064793DFB25D53A4639803CBE28013E5FBCA6BCE4C53E3C2EC4523BB07657BDE205BDBD06F820BECD58B0BC47FC0C3D95CA0D3C350506B9C076D2BB7E3C253D6D09383CD53D203DC272BABC22718EBD4A18F93AD28AEFBC52FE89BC40AC3EBD0A448DBCED8785BB884F0EBE49E9223C9B539B3A6568B33BCDF6C8BC188763BC5C1538BE7E17C93BE4776DBD070C4ABE386AA13CD3C3C63BDEE0A3B990D559BCA05445BBD010CF3B10C2D0BB862E753D3AFE71BD09786CBCC00B80BDC6BF9D3C0D9DA5BCC28008BED756E3BC39216B3BB471DC3B84DF46BDE879403B7CF981BD2B6F99BC7135DCBB92CA0FBD01AF8A3C22F1323CB08271BB676AD3BDAA372DBDC8D0883DD5845D3CC3E234BD49336DBDF21C77BCE0191F3C13141E3D657077BBBA3F30BD9CDB77BD17975BBDC6AB3ABCA5F4A03B8AB611BDF9EC9CBD46B999BB45EE183D4B950EBCE17A08BDFA365ABC62B804BD26387B3D763A873DB00A103D696619BD9F4AA73CC5D117BD5A59893CAC17113DF7B9F7BB61F80E3D2E760DBDFCA7C63BAB250CBDA388C43CDDD4BE3B829A5ABC38F22D3D38DFEA3B62D7493CE2442BBC05BD39BC8D1FB9BCDB19E73B448EF83AE1D7F73C8D44B4BC19F9A53A63D909BCF52C0EBAD996773C0DE5333C1C7201BBA6C2AB3C9AF7103EFC531ABD61E1B5BCCBB5CA3CB165B63C737D90BC20F9E53BE0916C3C1553B8BC702A75BBC3C7D13C2F4687BCB10C92BCE93696BD300189BD9A5E6A3C9103CFBBF362A13CC4C091BA6E4E02BDEC489BBD97FF4B3C2D8A35BC7F1D173DDDED333D0BB156BD1669A4BC7F3F89BDD9C99ABC7A2F28BD46731CBCCE7A213C2ED7933A7EE095BDAD13BE3C78EEB63BA467563D7C5FBE3A49FB28BC8DAA973B88D302BE21D13CBC3686E9BCBD240ABD369353BC0604B03C1584A2BCF4BEF23CE4DC1BBE6DC019BC380F303C44ECB13B6DCF633D6579353DD011B5BC8D0864BC83C14CBCD4AD423D97732EBC8275833CB61DB8BC0F2E70BB557716BEF5B2A9BC9A80C6BC346DEABB11FDC5BC7D5A523DE80C56BC6A400E3E3CE1C73D6747AF3B8D7420BC6D1C093D05D80DBD6970C9BB2E3D843C5624F7B91C52A6382DA339BC0BFD083C7D7D923CC669B63A50CE41BC934928BD4B973EBB340D823CB513593C1B7A8CBD3CCB76BC9853D93B8D5929BCCE134ABC214A3B3C1172B93CC1CAF63CBB08723C5A6B853CD5DBBC3C71229A3C3DFC823CF8ECAFBB335B88BDBC2DF7BCE497943A80A6853C5829173D69A478BCCE2D683B08DE4D3E27AB363D9D92F8BB28CD3B3B7BCCDD3B2BA1793D8359773DCDAFB43C5D6794BD316433BD77EF4E3DD70CA13BA4F9F5BDEC0AB1BB71F79BBD3D4594BD252E8C3DF3F4883CB9FCE73CF44F063D104B323DB3589EBDEA639C3B9B2B513DCB271FBC06FE6CBB7B31763C7D4592BC7CBDD63BDF4C9D3C13C4ABBD3A5BA53C6B26E83D97F41CBBD22CB4BA1D24073DCCD5D5BBBDFE9B3C5FBC1ABD9E13A23C238AA73C323EF4BCA03AA4BADF6C94BD897637BDC3B2D1BC84F121BDBA5AA2BC9FF491398A0DB73DFDC5B33C07505C3CBA5499BD6CF1BEBC5AB066BC4E52AC3CA3B2D53CFA31013D72EDBD3DFBA392BBFBFDBE3D540DCB3C6E2DBDBC55DC8E3C5872403E8C03593D670FBEBCC068C3BC844DB7BC9E4F72BD85DE96BD2475BBBB65A8823D0914BABC47639DBC19308EBDE669CDBC3B153E3D34E49B3D5A2E503D152A2D3D200EA23C3101003CB1D19ABB87401CBC5F4C62BDF95E803B5FB495BDBFE5A33B85BEEA3BBF848F3B87DB34BCB0C5313DCBCC7ABD8F5DDF3C44297FBDE83DF4BD96B94BBD95E374BCA8587DBCE704623C23790ABB28951EBD22CCCB3BEB63A53B76B1EEBCFD822B3B715B593D8E2F643CEEED24BD051095BDACE6A8BC083B3ABC8C16E73BEAA36F3D9178ABBC7D129ABB753A1FBC5B6F68BCB43CAE3D321196BB60A082BAC78099BB063712BE74918B3CCA2CD8BCA691EA3B3869023C10F3993D2C040CBDBBF62B3BD8B897BC1CFC39BB3448AB3D802DE43CF3CCE83CECABD7BDA143ABBD763A053DC010143DAEFA73BC6BD18B3CFFA19EBC08B064BD676936BD4343A33C7F6DD1BC62FA96BB091ACFBD037835BD268081BB6A1D963D280E0D3C944B87BCFE82013D6B530FBD4E40E03C33A57FBB769D4FBD24C1263D3043DE3ACF961FBC7B1982BC90BCBA3CF6C6393C7203013DA947DABAF9463DBC6F397DBB92EE5EBCD60727BC9D9961BB8DE234BB6902C8BACA94383B7BEF5E3C765E03BC1D4AFB3B290FA53C5188D43B588650BCFC1B7EBD3A9519BD4485D03C3B8D7EBC56F1E2BC53BA0EBCC080EE3C183C753C5BBB4F3C43F396B9BCA92BBD44DD4C3C369AA7BD40FBFC3BE61F8C3CD31C4ABDAD08F7BDB82F30BECF6990BAE18366BC7411353DB349D8BD29F2D53C69F21B3E9138C63CBF779B3DE5A07FBD5916A2BC810B0B3C7CF0DFBB4F332DB8CEE5FBBB8D5908BD2F079C3B6DCF83BEA1FDC63B434D25BBEC61EBBA8F9049BCE19ED83C54BA15BED775ED3AC0DD42BD049ADE3DD0D6E83C29C2183C5665C5BBF41413BD3B05F63CBB92013BB4261C3B53FAC0BC42E1C0BDB003F4BBA27222BDC2BEE3BB496598BB5AECD1BDE7BB77BDBD1303BD40FD83B96AD2C6BC7E9892BBBA3C52BBD28B9FBA5DB0773C1A6EF53BDD48EFBB3FBDA5BCA8FEC13C2225ABBD6680BA3B9AF0933D620818BDEA0E233CBA901ABE2B461ABE9108573C77A6ACBC0A2424BDCA1DF2BD1FF8F3BD6D0A143DAA4F71BD8808203DB5A7B1BD6758163DA490193EF86D25BD1B844C3D4344B7BC3C078CBCD2A7AFBD8B088ABDF679CB3CD38F703D3207843DCA1AF73A010321BE4EE494BC3EAA90BC0D5B44BD7164843DE47E64BD58E395BDD86620BC5086293D8752FF3CBCDAE5BB5DF611BCE457903EAE1D5ABC1DCBF83AB9C0453B150C833CB988683C87CA4ABC578B01BBD1669F3CCB4C333C9186F53B856D32BBFE66F73B6C5E4DBB235CAC3C8FFF25BC3FCF76BC974BC1BC0711F4BC9EA7A9BD9312823C842E71BE1FA78EBD620157BE86520D3B579309BC4B8C9CBBA34191BB5AEF8ABDB3D9E83D1438FC3C1495BFBA3A9C083A3095DCBC330347BDF8F3DBBDD4021CBB045E123D025648BCEE47283DF628F3BCE9E1C8BD4637EE3A11C7E53D66E0343CDE18BFBD47B44E3CD1EC05BDF3E944BC201AE2BB16F106BD88278F3C3EE5C4BD2F15AEBB33F0AE3C75FF863CA0D4FDBD1A9DB33B5599ECBDE0FAE5BC642CD0BD6653FA3B2BA57E3CD70D87B9D224223EF037F03BB01FD7BB6331BBBC99A1E2BC50999FBBEE137A3BA9ACB43B40DCFBBC7665353CD4C7523C9B149A3BD3E6843C46AE38BBADDE003E6D7E9B3C9E4EACBB30FCFDBBFF9F50BC2BCFF6BDB357B73C578503BD679244BCC73EC6BE252CC53C20AA363D3221CC3BD419ABBC6FFA0BBD9993383DC622063D8D9D863CB69787BCD6B38EBDF676A4BD8FCF003D906765BCC98C86BDE14EAEBD6172E8BD2CF65FBB80D3C9BD06DA313D3E2A64BDA30AA1BD6C01873D63D9F13A164B37BDF0ED91BB5EF390BD67DAD63CC1B17C3B25818ABDDD4CF0BA8A3C17BC59C51C3C0A4C09BBD497273B1C7BB53D785C14BC51B1E4BC14A2063EE7E93A3B22BBC8BB748D84BB905F383C5D2F55BDC7DFB3BDECD9DDBC5142873DE3588E3C0AA83C3D62AC9EBC820299BD067F9F3BB047D53D20CE1C3DA9477EBD9ACD9FBAE6B16A3C8E7C1EBC8073EF3B4BC801BDAFE595BB003994BD526D35BB6E9310BC8C3F67BB1A43D3BDC3CA52BCA095D5BD7F78DEBC8AD622BDEBCBA0BBA250B5BBF10432BC89F056BCD80FBD3B1ACFDDBBDA1A91BC028801BC9BB35E3CFDF49BBB6E61053C319E2FBD065F4B3DAB15F8B8D03F113C5D7E603C7172773C0E9ECBBCF4B7EC3BE0D2AA3BAA7E3CBBD247DE3A1D2D19BBA041693C22F7253C1A20A0BC0A85E93BFB361FBC7D981D3B7ECC29BC9F1ABCBCF06078BB7C294DBAE1AAA63BAE3392BBA37F203D098B0D3CD6B9173BD487223C5686A7BCF5FD0E3B254B58BB623CBA3A2A1089BCC92A5F3A521792BC606F583C84D517BC459FCABBA0E609BDABB2E53C65AEA8BB3BBEE03A83BE55BC963A45BC3FEA2C3BE1BBF8BC6F01FCBCBA1E16BDE11F7C3BF35464BC0FD0A8BB2A3C5F3A63A00F3CB0F226BEB328293B06BD8FBC0BE4F6BB1C6A93BCE9FE43BCDA84753C11FDB7BC7BB80A3D5EA22BBD6BD9ACBDF1D7C9BDA9A59D3D19894DBC8F94F4BDAA6C0C3C9D9036BC6AE7913CE846673DA509D53B5FBED3BC69CC02BD67029E3B6701A5BD0B93ADBBDA3813BC94B8BF3C43F94DBC67A7C63C602FB53DABA4E0BB45BEB0BC64DE9D3C60780E3DB7A80F3C5FEA36BDBEC813BC1B580EBB30DFD2BCB4CB7DBDFC1463BDBB4EE2BC15B792BC583B173CED9946BC91DD4A3D9AEEC9BC490E28BC9C655DBCF8E1ECBD8FDD4E3C2E7421BCD3AAD23D970E4CBDD282AD3D4EBE303CBEC0CDBCFA064F3D70C0BFBE85EA383DFE3715BDDE48DEBC37FB4D3C54AFF3BCE4B2F03A66FFB4BBC09FBF3B939DED3CF24F7B3C67F864BBA8B0E7BBDE78D1BCF73AC63B3DBB26BACABE6B3CCE5E1BBCBB84E4BB527688BBBD690EBB522D25BB0CEEA13B452720BD33127E3C5B67823CB4D1093C6B3B0CBDBAB2C9BB23ED5E3C67C5EDBC8F5D29BA69AFE9BBF01281BC076DF0BC87E36E3B5D7011BBF2268C3BE830F1BC7AA351BD471BA83C75E0BABAFA5B193D4D832CBCAD1D73BD9BBAE7BC4E9F273CABD881BD3C854DBBC2388A3D0C745FBDE76199BB28AC5BBDCC4059BDC4F83CBECF5C5DBC904E88BD21D17A3BFE8916BD4D94EC3B7711A53BBB0E1C3CA7E1A93B92725ABB4970EDBB369619BE204574BCA1B086BCCDF322BC9003193C12B04ABD81C038BC998A693CD7A8C5BBAA3CE03C2A3E1CBE22E7B7380A0F0E3D13E766BD3BA8ACBDE921473C1371C6BD2C86463D78B0A8BC1BB30C3B0D822B3C7A6C80BCC4748E3C4CB2FABCBB9DC93BCB7BE9BC7559C23D3BC3F9BB8A311F3D5820953BB1A1123C3BFE263D59E80B3D723AC73C134F51BCB493A53CF3FA3BBD1227B63D2A2F31BD30CCF1BB5D0685BA895313BDF3DC88BDAF4F7FBD64046FBCF13ABF3C8ACD9A3C4C99E6BD907E42BD8DB01FBBBD1248BD90B2D13C10769D3DF1AAE4BC9EA85EBB82F96A3DAB7F223AEB3AC53CDC7CEF3CD9998B3B109B0DBE959CBCBBA7C711BC71491EBCAAB6E53C9031CC3BF1AFE0BCDB48A13AF4ABA33C91290BBD7E0D79BCA73631BC7B4F0FBCAAE97DBB10252C3DB56015BBA494273DA8F2F6BB0549BBBD6C71853DDDF27EBC742CB03C45680D3D57CBACBD86E114BD98E269BDDA70FCBC520B9E3C590C933C9349B73DAFFF843B6505E2BB6290B13DECCF2EBC42BC9EBB26F2D7BCC30F37BE99DFA9BA486B9F3CCA0D03BC3BBBD83C589BCD3B0EBB4BBC9783D83895A4673C8474FDBB23AC623DC0B81FBBFF96F23B1AB1F13CB58A9F3C7D8CA73CACF84F3C7F0551BD724BB0BBAF77533D6A4E9EBD4F87E23D49363FBD3DE5E03D8BD9C4BCD6EF9F3D82081ABEC26FCF3C78BF653D31AA233CCDA7423BCB8CF1BC2D52F4BD32C744BDBF1213BCCA7756BD24F6153D872DECBB750501BC6E3A423C9D708DBE2003693C243E34BC0C1DC0BC7B42E6BC925C3EBBA2F8B23CF5F92A3C6585233C743396BCAC59593B82223DBB87A8CCBC9268663B7EB43EBE1928FEBB6B770A3C7C089C3C727B933B2134BC3DD1B0D83963D1BBBEAB18313DB7D4A2BDEFE1083BC417973CBD46933BDDA81DBC0D6CAE3CD7C300BD69DA20BD0338303C3721333BE609EB3B8F892B3B672A4BBCD4A4DBBC897FF93B9B2A81BCBFD0A5BBDA92453AD2970FBCBC5654BD0FB759BB6552FFBC41E3B1BB0AB84BBCC46CE63C4E7F363D177AC03CB3EC103C1B5EFCBCA95198BCA804EE3CA752C23BE16052BD52E4E83C8A7A57BD0584273CFF1AE0BCE3D366BCA07D62BD97E513BD21DA783CE2DD883B742B43BD3C69F5BDC1BB4EBE56D3C5BC2CBD33BD9C492C3C042EAEBCA0C6F3BCDF918A3D294C013D702CA03C36BC02BDB0EB99BD8C78003C96D4BC3D894F9FBBC6E5F1BD7D865FBCE19750B91D93BBBDC15DF1BA64EA8BBAD8A2AE3CF9277F3C471AA7BBEC6EF2BDEA8D0A3A7F0F2ABDC02239BB90749A3B942226BB139A60BC5BBA023C54A80FBC28CCB43B2F27CDBCF3E6F03B0B24A2BB5BF1D73B92DE02BD0FFC47BCE32D153CCE6D87BA89C0613A6B0BAF3C400ECFBD8C5054BA3F7310BC62B3673BBDFC52BB917DBDBC0E468EBC2E4002BD9A7F2ABD7166423D8C0A173CF54F78BD3A07363B5BDF2BBCF8FC213D201C293DD188A43DC717BCBC1573C2BB18C6DCBC335C25BD1AADE33C626DA33C57A5E4BC8C96A1BD4598B3BD55C17ABD9FF926BEC7B77CBC795C83BD888187BDC0E9433D65367DBDF178893D20B723BCE00EE33D5FDCF13C4EE12ABC4995A4BDCC3B8FBB0F0AFAB82D863EBD76BD4EBA02CC76BDFFF2D53DAF87FFBB7427BB3C27BC283CF5858C3CA8E0953CE431D2BEA047123B216AD2BB55D10B3B2D3D913ADC50FABB3D2656BCBB6E1DBC5CF3BD3C79C61A3CFB2CC03C7BEFF63B29C90CBC6263613B4C7B78BC002983BC42F5F0BB431A21BA344FE7BC9271D53D87CD673CAF68BDBE2075AD3C454AB8BD6699733C50F545BDF60EC737082915BC7333BA3C1C8B443CB432EEBA527049BC8F14C53CE25EB8BADBA2F0B9054BD3BA218B6EBB2229CF3C60108AB9924E28BC081C28BC21A2CABBE65977BBEA94E2BCA111AC3BCC3B6CBC14BCFBBC0D9E213CEA27B339249D8FBC92F7B6BC8E0DEBBCE371D53A75D211BD31FA78BC59D5B93B314C32BCB84382BBFD16843C4F2A9DBB42D28DBB286BA63C4D6A223D5EEF663CD5BFEABAC2750ABD2A4A16BBEE01A2BBAAA5F63C76FD703B9F6BB2BD26DF56BAC555AEBCD4D8E93CEDCB923C1A6CCEBD139945BD70CCFB3C32A5873B49B195BCE876BABC852A913CD503C63CF0E9D23B14850BBD2B12BFBA1AF538BB06D8943C01AFE6BD92E3923C728E9B3C8CCC4FBBCB93F7BBE35241BEFB97923C6A9426BB4EF8D03C3E1A003BAAECAD3C712BC83C9A6DADBCE781613D90C674BDE6BA073D3298FDBC7FEFDE3C2CEF4ABC6782FBBDBC2CA73CA052D7BCDC08593BA05D0DBD5D9D0CBC16A163BD15C1FCBBBF1931BB4645D1BB1077CF3B60C8C5BA1B097BBCCFF8FABDEB6AA3BDBD8F4C3D5F040DBC9F9D13BD50B092BCB50F69BB488730BCE460CEBCA6B7803BCDCED8BA844CCD3B4CD617BD1576B43C92BD6FBCB64E63BCA72D28BC937B323DDAEA82BBE3FAF13C974B7FBC0364083BF9A1503DCB77523C5376243B05C406BCA37DC8BCAF4D893DE44D38BC7C5C983D73C4CF3CAF30F13CED1455BB59D696BC4A63993C608383BC06BD56BCF42EA0BDBA91CABC88F86DBB89A967BB0C8CB23C70A8E93BA480943CD0B2213C87AAE33C3FBC56BD893C203DDEC4F83CA514F73DBAEF9BB9A8C1B4BD6196263B8DAD9CBDFAE6BA3CABBF4DBDB248EFB9D1C565BCF341F13BFD66973C3F47A83D9B59D7BB521829BC4EF0643CB35AD3BDD77ECD3C936BCA3B6FF2403C80232CBD2F32223C2CA7D7BBFB147BBC5672513D7FB45A3C4641183C8BE7513C9BAC4DBCEB56BC3CF3E1A239BFDE583CBEB25ABC1C3237BC04AE70BC2E41ACBC03B0E33B0729303B6991D9BB0643123A6ED00C3C449750BC1A9314BD3BC8BD3C3CC99FBBE5AE82BCDE50733C583146BC055FB6BC9FB9093CE05D32BACA7A48BCDB0E873C104BAFBC3BC8CFBC71B70ABD5F4C553EF50C78BACA9FFEBB16C14C3C7676653C8E95B33C71F3863B6E6F073C5CCA0F3D495F89BC44F6293CB658F93BB1F03CBC0E60013B731DA6BEDCBACFBC300F50BCDA92573CB617F83B191993BE264836BC993DFFBE837101BE210CC43CBC3A133C206B9C3C3F8DC6BC6B98ADBC04FADEBCB752F93D0DFE5F3C86A408BA0C7715BCAAAF843DEB2A08BD583E4B3DB274693C09AEC93C64CC9EBD356AF83DAFC9523DF6E23ABDECBDC8BCAFBCB03C4D5382BA0C28203DDB2932BC5A2759BD20D2AE3A8A1C793C4E42D63D66BD5C3B3C93673D7ACC7F3A197E2ABAD25EF6BC84A273BD3AC5BCBCB26EC83D920F023C1E218FBBAFFF883DCE0E953D3BCE8E3D6E81DA3D71B5FE3A918B593CCB0C3FBC9DD308BE01623ABE3541C0BB6604BABB703FFEBCBADE953C463B873D262246BD9998A6BC9BFDBC3BACEBA23DBF9269BC83F2C6BC7DBA043E9036E0BC8A97CFBB8B2416BC264EA13DF6B2B93C80B78C3D0278063E3719D73CDC8681BDBCED923C3A857FBC019389BD331BE1BC88CD11BC29D65EBBAC77D1BD6A5820BD823B983C911A663D9DCFC63CAFB9AB3C9A5B95BE8ED234BD38583BBCB59290BD64664C3D1B55DBBCD0BD813DADB452BC5456E53CE9CA673A2492B43CD8E5D13DA887913C75DCDDBD496A37B9E6162F3C34AB25BD337B64BC269C38BD976E673D70F0A7BB00FABB3C6CEA0ABE6266153B382914BC7ABD183B638645BCCDAF25BD8AA0EA3BD96AA93C55309D3CB6025FBD047D6BBD38749BBD6897A9BC085556BD4BA8BDBD683C9F3C41D93D3C2F41693B356EF93C864B64BAF087033D50BDC13C8004E83BAF9BCEBC12FAB3BA69A5733B4D6BDC3B3BE4A8BBDE862C3DA755BD3DE7CA333B809B5BBDE1087B3C9255D0BAD826D6BDEBD56DBE531AF13BEAE0EC3B9D51403CDA64B5BCA7C0703CAE8CB1BA594EAE3A36752B3C78AE66BBFF8A98BC2CF8123DE2309A3C34E092BC22C9F93DD5F42A3C1BF85ABB2FC9C63CCD87923CC8D38EBE618C613C0EBAB13E457813BFCFD186BE5082BC3CDFA4C3BCC6A70FBAB2FD87BC3238A0BB4FF407BDEF1A95BD94B558BC683F163C7964B1BD67B800BEE3ECAABD213145BDF4BBF33BB0646D3C524005BEC6470CBB3546363D2A59283C4C1B343B6AE73C3D3BEA00BCDD6AB4BC9E57413CF0C6D83B6DC104BD1B20FF3C85D79FBB3C2401BDDE1AF8BA26E4183B1A02273C4F6E1C3D6985B4BBF23E61BCD94BB43CCC9D84BDE6C73ABDA41E2ABDB02829BCC52E4B3D5FD387BCC5D1A53A2C3EB1BC8E947C3D810E16398AD6B0BC7D78AEBC94D00DBC6E6FF23C3A0BAEBCD7E3FD3C0C3F71BB02595EBC410B4D3E84150FBC1C8D0CBDEEF0C3BD46511EBC123F4A3C459D8A3B4AB1CE3C921692BC1B70E33DEFDB35BC25D14C3E4678F6BCB8B39A3BD9BC44BD"> : tensor<8x2x1x1x32x32xf32> %cst = arith.constant dense<"0x87DC18BD89795CBDFF5FABBD1791E13CFDA2F63B8A1EF73CF2BA803C5BCD373B69B7463CD5458B3C14DC4D3DF1FF87BD26A0F5BDAC5039BB5E62BE3D25DE953B84A3883D272234BA0A96FEBB3148773C838EA23CDDD8C53DF3D2DA3C1FA03DBD3C370A3D477E403CC09191BC436910BCA7CEDF3C33011BBE03E82F3CD5D66C3CDBFD133B8EBC6BBAE2B9713D3631A03DD19360BC7E1BAF3C5D44203DD164953DE09E82BBF1F7843DF02CE4BDE05C6F3BF11019BDCAC0073D1FB90D3D3F7735BE7508D83CAACF373D462DD23B8204C6BBFE704FBD1E4486BD8648983C2A7807BDA952C4BC7A2F163D3C3487BD0306CBBC1B6ED0BD198C9F3CD7AF1B3D30AB39BD172A213C890991BA753BB2BC4F42803BE8957FBD670A84BE3740B9BB7B1180BDC1ED09BDBFDDFEBA50BD623C7B86FE3B614AB83C1ABCC33C685C2A3C89F3D63B165679BBA2030E3C0B795BBB1B7CAB3CE48A09BCBEC2F93CD51D51BD70261BBD956C18BC0553093D3EEF653CE1714ABCBB638DBD7F8C853B909F8E3D87BBB2BC33AB95BC494D82BDD56935BD0C13783CCCEF93BB5A4AD63BC50985B9FE42083CC63CA8BD9855343D564FAC3CD35AC33C4762C93B78A6293C72257EBD645285BE3F66E4BA8156DDBC19B816BD90931BBD8A4F2A3D3B2C2D3C08FD2EBD816FA03CDBE44B3B0B49B43BB3146FBD5E84E03CA86D11BEA8EE0B3CF92459BD38C91B3D9177A1BCFCA226BD11DD973C69773DBCDDE18C3C91C5D03B133E89BD5412893DF59BF4BC39460ABE9ECAC3BC19A65CBBA25A2EBC7A7380BC2286093A12483BBD27B8D2BB884A5D3D72C4AF3DFDCF9FBD5F9C9BBDD857AD3CF4D7133E9586FF3C13EE483DF9CE893C6A992EBDEB8E0D3DA9BF09BE188DFFBD1352DCBBD110833CE38F44BE92BAE73AF64424BE0A18F6BC1B700DBDEC44FD3CB4AE683CCD2448BC98CE4E3DF69C1F3D2B89893D11FFAF3BE9F2D93D92630C3DA2C11BBDACBFD23BC6367D3C54F7D03C25E8223A66171BBD9169BCBBAD8C2F3AF32B17BE8E5B8DBDF495723B86BC7DBD4D6AEA3A49F3103CE2003C3CC7676CBDFE7CA9BCE5DDB5BB299868BDDF1A853CBA8FF4BB9C00923CE6DDF6BC3D10963DB632ED3D0C162FBDCEE7083B4A34203BA2DBBA3CE11E6C3C9A1B37BD80C4A73DCB8ABEBDB028B33D126309BDE73589BC0307B43C70F5773CDEEE9CBDFAD709BA918FCD3C75A9F53B40EB45BD124025BD09C96F3DF22CBF3D39DA163C847BD13C1F2ED6BC1727D33CF4845DBD9423BD3C0B66C43B5DD2B7BA4814593CF563D53C3A0FDEBCB94F8A3B8331AEBD40C6ACBD7A6B4E3D998F48BD4D22D2BC4E50B4BB8391F3BCAA5F14BD888846BE66A598B9801B0E3B75CB123CC45A673CB81B0EBE277031BB5EF1223D1FDD6CBC7FD88D3B39DF393D6F05173D1378A4BBD3F860BDDC6A013D173AD13CC326F83BE8290E3C7387C83CEBFB15BDC93EF1BB7F575F3DF55B3BBD0F2A1C3B5C24FAB90F513A390E1727BC010452BA7373BC3C5FD3A7BE6BEA7AB9B4DE3EBD6FAB5A3D33DC64BD7EFB463EFE70103D6C51A4BCD8DDB8BB97FF953B1BBD293D62FB0EBC64292CBDEA8C8CBEACE91A3DE922D2BCED05A43C1A41F0BC414F16BD675EE8BE1A61893C288965BBEEAFD43CD4B78FBCE8BDA9BC9FBFF7BB8D74C33C9F68BFBB74F7A1BB505171BB55FB2FBC6AE0E53BED38843C7F96AABD9B680DBDB4EF11BC320EA93CED1CBEBCB27032BB25390FBC994D5ABB710100BD96BD033C15FEBBBC7F1220BD560D54BD2AFB503D6C17BEBC7D11F0BD78B5903A77E90BBB02D0243E25DCD13C5B3582BC131FBCBCD5911E3BB627AEBBDDA31D3BB5D729BE070005BD333C21BCCD779E3C7A628CBDBFEC67BCCC685A3CB6320F3C06867CBCFC0309BC036E543DE24434BD1AB9AABD07AFA3BB241CA4BC0C6E1D3C23EAB13C23A60D3C6D08ACBC2DB8A9BCBA7626BBA4BAB73B3970783CACD32D3CF6C6C53B5698D3BCE7FD05BD536E843C3182A5BC256C60BC0F18793B7058A8BBE0C58B3C7339DA3C67EE1CBCD05BD23CFFE13CBCEE3088BE61EC313D547E9F3DFF1436BCE47049BEFF113B3CE17DD93D4EE5A53B74E9483AB870B8BA576124BC6F61713C61CD9F3D5F0C0C3D9891323DBC2140BC501011BCF27A44BCA435E13B36133A3BA359CE3C675BC7BCE57441BE9AC546BC92B511BD843D333D4EDA6F3C0F7398BBE7C57B3B9515A23C2236CABD1B0BFFB9DF2FDE3C4E53FC3A66DD723BD095793C68BE2A3C138EF2BC10E5BF3BD0638FBB9AC08A3BEA61863D287A86BCB9B01B3B77BCE63EC057893CDC5FEBBEC2F0F7BE561106BB3268EF3C5ABB0CBC4CA7CCBC33D8D0BBBD4EB2BCBCCE43BBA00DE6BAEFC9D3BDBD7E1EBC6168673EA6875FBBB54E3FBC9218F83B5CB8E83CF07847BCC1948B3C25A5F0BC7748DCBB855CD3BD7EA980BC5517943CB11C81BEC2F07BBCF00072BE3D57D2392E833FBC861A12BC4BD9D43B21F1DC3EBD8AD73DAC2FD6BCB9259C3DC3C6103D76999C3C4397EDBCF7CBDDBBDD5D85391D748B3A841DACBC10BA70BBCF9EA83B295A5CBA657E7D3B779CF3BC5C564DBE8A69CF3B700F03BC12282C3DA676ABBD7AD706BC3E66C2BC13CE1FBCFBDD84BEC3EBACBC0B63D93D1F3F5B3D9B79F5BB5E0101BC22A61A3D0C4DF83C39AC09BC88928D3C1EB7833CDA5ABD3CB8CF40BC6EDB753CBE94F7BBCEE9B03C5D8470BB65C9FFBC1E0CAEBEAE5660BCF15C6F3B29BB4BBC92F5D53CCE3CDF3C248ACCBB0AE634B94E9407BD45BE5A3C7A8933BC993449B86719C03C4E1C323AF81D243EE243E2BC3D22D4BC525F89BCF38706BDFBFB743DA3DD02BB118D693C632B1FBE1ACEF3BE77C6F0BB45D4873D567512BCA9D8EABB9A9402BC408F9FBCE0F016BCD40881BC6450B73CAB9C23BC42CFEEBC1D587B3E76FA403C4A0E043B71B9293CEADCA13B90E4EEBC430787BCED316B3D441C333C8DB3103D5009283C47E0ADBD02DE63BE2018CABCF32E243DD3C7003C400AC43C0DA8AE3AABC5DBBB9E8DD4BAB489CABD7A7F9BBC0EB16ABCBE7504BC6766A13D80F5A3BC06049BBEB056C3BC50F84E3CC5CC2C3D361CFD3C56EC0C3DAE7063BC4AE352BCE45DCC3CECDEAABC26F8E13BC2469DBB9BB2023DFD4D2D3D195792BE276B733D8C7F3A3D7FC3ED3B774890BC87E240BCAE72243BF0BA7CBC3A3A93BCCCC4F239713215BCFF9BC6BC06EAECBC2F03F8BCD65F3F3B70F697BBC0CF553C102805BD32E344BE58B01FBD0556BEBC6EF8CDBC3538573CDFE2CC3C21AE83BCC62FC63B3354A33BC67109BDEB56B73CE214E73DDAD736BDA93A80BCF9D930BCE5D88CBC611FA8BC17B26CBA8B3A653E6303883DAE63B73B9D2218BB105A4BBD78FFA03B94D951BB45DFAB3C3D3E38BC821E9BBC45F70BBC5F6288BA4E402DBAB1FEBB3C0AD285BCBEB2FBBBC6A314BDB433313B3342C2BD3EBE6BBCA5D6BABCF938E13C43631FBB478E69BCDA12C5BDE22C803CE27E30BE6506A83A6D67EDBD33ECACBCBD58E53C22C58EBC4D9B3CBCCBABFA3BB5AD1C3A4E84E33CC66E8ABC9B92213CB496163DA91C34BD48749FBD596C1ABC79AC8F3C26F010BD68D4B53B18457EBA70B69EBC6AD2B83BF1AD14BEA217CD3CBA7D683B7EF0493EEAD02DBD2781CDBC4A2B213CD11049BBAE88F8BD2B4D0EBD3E2C19BDD0AE9ABCE60B383C0D116FBC35F4523CBE4B733C1A337B3C4917D0BB4DA237BD2B08323D5C1895BDBED64EBC4989D63DB57A103D34EF2ABEE1A7C4BD3D0792BCD2FA7DBB96AAF9BB0357253C4C7B03BD00CD08BD108AF1B9BC4CB93A87FCAA3DE9BE9138DD983ABE1FBE0D3DF307B03806BA1C3D4DE3593BBFAF223B05E23CBB9D509CBCEB35C83BF083093E7ADB803C00AE74BCBDB98DBD460ABCBCD3ADF0BD931B503DE51C21BD9F625FBB18C40C3CF625023E15AC2DBC3B0E4F3C2BEE093E4AE4F2BCCDCCF1BCD467DC3B61349EBAD9D70B3D05BAC2BCFDCFB4BB8533A73CC452E3BCCE8A9BBCFA7727BD7CF6E9BC58D20E3C3421103D87C926BCCF7CBEBB8FAEA43BF17E92BCCFB38D3DB76AC5BCD56D193ED13B783C885266BE198F76BD2C691D3CE8C82A3DEA6932BB2CB7C8BAE0D1A73CA13F1F3C32B446BC41D2E9BB919E62BA978E26BC9AD2A1BC903B5FBC21521EBBEABC4F3CFBA59CBCA37B293CDD48543C4E6A1C3B71B288BD4AB0F3BC8D87AC3C5B368E3CBC50D1BC869E80BC9BE4943B00B99A3CB33A42BC57FD153B734179BB7871A2BC217C343CAA4CDFBC3E52243B9FA231BE4459AB3C736A71BCAD1D143EF5A47DBE169BCE3CB26FB2B97F8C80BCF24CB1BC0CEE61BCEF2D553C80D66CBC09A5D2BBEC0B853CB76CBA3C8165473DCCA6163DF90D113C9CE95B3CAE489B3B3607C23C342DFEB93DEFF9BCB0D689BC52D200BC804294BB26D0BFBC27283BBDF127C0BBA7A9583DBD6FBD3B791AB43BB9B7913C2A7023BC431481BB125DB2BB723A233DA9C55E3C4FD8CFBA09FDF8BA00A9FBBC0DD8F13B0EA40A3CB1858DBCFB5B4BBBF3474E3D83C4E3BBF5449DB9EFB94F3C2A7E603C8F3CF93B42BD883C618563BD1DF3AF39D5E9F93B17074B3DDD28933CEB465FBCC9DC3E3B9C98853BA8573E3B13C2F63C187E3F3B35831FBD454FAB3C5CF69B3B7531C73B1E09DD385DC1EA3B1C9BB53C0697F3BC413CA7BCCEC8ACBBC77D76BC01DDC23C5A0DE9BCAA1FF8BCAFDD53BB47A0F9BAA46F773CA52C223C833B83BB421CC33C307CD63B555A0DBD4908083C5B8924BC9E67CCB9309051BCA96802BC936F69BBD64782BC6FCD0BBD1424E73BD2750EBBED7959BBA70562BC13BC703B32DF673C464B913C02ED5DBCFA71213D75CCE2BB86A000BBE2DB703C1C83513DDDEF9ABCA2A6C5BCA3B514BCE03C9CBBA8ED0EBC8D629E3BC8587ABCEED2533CF27240BD8C82813C73F280BC1B2F69BCFFDAFDBC26FF60BD4F863DBB68BDABBCF649B2BC76835CBCD436313C73CD3CBBC936463DBEEA2FBB8854E9BC2F32B6BB5B6CBABD5586043D16D69F3D73E57A3CFEDAD63B8C74943B06A086BCB0499C3C5F47263CC706E8BB85099EBC8099D8BBBB349FBCC615D93B581A41BD658A50BD74394B39C353393D8FED2DBD9E14AF3A9611353B68BC17BCBA3ECABCFA9C413B4C231CBC28ACA63C9F4B9FBCBF71873C44DEEEBC0D822FBBCCB8843C21069C3CA319083CCE976C3A6324E03B3C85A93CAB0B71BC176A82BD9EE821BC4E6D16BDF70B30BDDF7E433CAE1AB03BCCAF06BC9589D43BF565B93B2AE309BDC40C47BBB0038A3C0173803D68960ABC7D808D3BCEA024BD818E333C3318023DE5148DBA97F158BB72FDDC3B9CCF8EBC21D020BC8C5CAF3BAD2500BC657810BD0DA4C53C2E6EE23CA94D043D0919C9397C2711BC786501BC025FBD3C13E0C33C3CC96A3B794CB0BABD014538121AC1BAED8485BB0541D0BCAED816390EA1E93B1CBC7C3DAC35C5BA1693EABCAEEB26BCC773133D4823693C4A24343D505461BCD48F16BC2BACC43CAF699ABC1D22EBBC8013973C44DB0BBD88218D3BD8C39DBBBEA2A33C553147BB1ED308BD9332253C3E14133A7851003C2604F3BCFDD79EBA544C3BBC688F2CBC1E55523BD0FA64BCDF59F2BBD643673C01FF4139CD04F9BC08482E3B5B7A053C24E3D7BA59BF383C709FA83C3754143DC7BD553C2A8DB63BC276DB3A3BA7BE3C8BBEA4BC6077983C3A1309BC6468A0BC25D367BD6E5BECBAF8849E3C92D46B3BB6A1F9BCC77DCFBC2F583FBC1882AE3CA98D06BDB7D439BB86F2373B46992F3CAF25D53D6D1E1C3DFCA3A63D87A8923C5E2C593D78BA7EBC3FBAE53CA61B4D3C16B199BD9E2F9E3B849A69BDC93D3D3B8A7B563A5B6B3E3D56CA433CAC13523B9CE6113B38D021BC9AD608BB097EEC3C9DD89B3CED3D35BD3DB94D3CD5EEC43A1BF99E3D776C1DBC7081083E67965A3DF28BCC3CAAA2AA3C0F0274384934243B9BF6523DC230B43CD27ABF3D9AAB823DC0F7E73D350DF53905D81DBDCF1BCDB95DA2023D6B9A02BE7A0482BCD7ECBBBB17E7A2BD2FF7403C04F1FBBDE951063C5D028B3B5D1FB43B5382673AE4DF76BC28CA693D52778D3DF5B58BBC6C7D923CD3FDFD3A9AE573BD9C4A923C402713BEF9E7D93CC7CF463DEBFB74BC2F3F27BB80B60DBE96E432BC628055BC702E183C605E84BCDA73503B86F869BD4B8AE23DB1CF1EBDED4D76BDE9FE1C3C3B00D5BC26649D3DDE2AFDBA7D47013C85EC123C4ED922BC5CEEBD3C6A834F3D90C0A73DCA22BDBCA4AEF23C29BB31BB565FA93DA3376CBC5A8A2E3DFFC5A23CCCFC073DACAF01BD366A90BC95326E3BC9FD6C3CD54CFBBD552A7B3DAA8F87BD4725A23BDCA682BC08E747BD264ECD3C7F8C17BE22A8BEBD146EB83C0923D8BBE774B73AA3CB743CB47C5F3D8D9C8FBDAE939B3C97CC903CC59411BC442A00BEF010413D0D19F53CA44B1A3DF60DAEBCCBC9C0BC5F5B80B920F75A3B78C3823C302B65BD02F5B53C0669163DE18464BD5A6AE53CB10F8BBB0D99A73C33E077BC5C7D73BC12B330BC1D2828BEF17E3EBDB263A4BCAC12C2BDADB1503C6E90363C159E70BBA02220BCF67D37BE53A86EBD1BA7663DCAFF613DDAA8E43DB9D1B8BC87D20D3DE4B68FBB5D169C3DCC4AF3BC9F6C083C7D7DE4BD8041C4BBD92D03B9F4742ABD8B0A533DEE1D5DBCBFEDD73C7BF425BE38A2F6BC3E75FCBD6D36E1BD549FD83BCD57D23D764AB9BAC44B4FBCD6B18ABD9FF5F13C88F0803BD0BB5DBCCACF273D3ACFCDBCDD18753B58D88EBB42E509BC3D414D3D14BB743B880368BC6480B8BB392D6BBD8E9F9CBD6F17DBBDCFE5FDBCAC625DBC5D39F53BEDB2133DDAB6B5BB87455BBC3416EEBC126C973CB3C74F3DCBC7053B7D7B533D936B27BC853246BD5FEBF73CA17E383D09A20CBD928023BCD51C873C3634A6BDF055EFBA6EA9F5BCE1B8863DCF7682BD8807933CA41805BD82780F3C36D7C9BCEA81133C08CACCBC488653BE63DFFBBD029183BCC9A505BE5C7C33BEF8AD11BD4A464FBD077C0E3CA94CDC3B71EC9A3B77B21CBC49CD25BEFE3DADBD948C173C3884C8BBA755AF3C90E8E1BD34A1F6BDCFF1663D35471DBDD963B8BCBEB0063DB8C274BC0A0D0E3EFE9E363CA54B9B3D87BCCABC6A5A1DBE8EB0D83B281E97BD06C08C3CCD807EBCF0CFEE3C69E5373C2CA032BDE13CD3BBB665A63B3FB3A13B4E1F163BB66FEBBCC02BE1BD11E5D3BB7CC2B1BCDF15DDBBA5A1AC3C4A3A2D3D89DF0B3C040C873C1F06293D12DFBEBB8BB1EBBD3781883CB19E31BA88D7663D6C5BF4BC8565593C26831FBAAB7A9C3CC39BA2BEC235063D5066583CA7D7A33A7D1D21BC12027FBD43D59A3C639E81BCA403603DF82BC63B4D0E2A3B0D3A8ABCDA9407BD952A94BC48F2AC3CE8AA8B3CA2FC863A7EA54C3CE822A2BDDEB4BCBD3BE0D63CCF0CF3BDF452F1BCCE193C3D0611FEBD0931CABCB88CB5BD04BE2A3D8D9EAD3C1C2307BD2F0778397DA0223D94769CBC51378C3B3A36A6BD3CDCC1BC9D9309BED48A273DEC9AFABD55870BBD2E72313C82BBDA3C8A88053DEA438A3D5D716DBD03D7CE3DA18A8C3C457339BC25B3323D5634903C6B7CCD3DECF9913B1AAF53BD7C7E06BB9C8488BD45AD49BDBF33D1BB2ED663BDEAEB1F3DA8480E3DBC79333CD9F3493D236C17BDB81BE7BB01CEEBBBA5AE9FBD55D9B13CCC3DC33CCE50EF3D7E7B26BC6D59293D93BD86BC90A4E93B0A8C14BD69EFCA3CD44B95BD376D5F3D0965F5BD67C7CDBCD6755B3C9887B13CD8CB95BD03938D3D7BB91ABD4625973D926B87BC1F002A3B0D5EB13B3B132F3C59DF023E502D28BEE0E1DB3B83CCE5BCAC8EDB3CD68B2ABC2EB2FE3B06790EBDE91C83BB62870A3D2FE441BDE2375A3EE927C5BDE89A803C663C34BDED006D3DB2B63B3D6EDA173D667C7CBD96B6ECBBB0701EBEC2796A3CF83A2CBE9B32AB3B264EF5BCE81A1DBBF9D371BB64E321BCD7AAA5BC4E402D3CF13065BC651725BD577ECFBC22530DBD8FB8973B299D35BC7D859CBD15A3F53BE85EAABCB5522A3DED3EA8BBD5D9A3BC2ABB8DBDC0FCABBCE851073C8211CE3C972C50BD6BC6B93CF3B5413DD4C0433CEDEAD1BC367BA73C0365133D9A10173DB89224BC84A8843B657CC63D952EF03C35A8343C3B8FD7BC3E4DFFBC6F8158BDAC6E133C1773883CF3BD593DC92DD83CC0AB07BC6903AB39C824E0BCA9D454BD4E4B13BD03F9733BFD9BE13B068C9FBD2EF0B2BAB812B7BD276F09BDACD85D3C7C7EA8BDE3DB693DEDB8403D3DBD0CBD993A1FBDBFF9A3BDFF6EFF3BA99DFDBC507727BD7697B33AFD4182BC3CCFBB3CA79F4CBEDC4B81BA34E4763CF5D19D3DCFCEDCB9642653BDA0086C3C8714B03B4A73423DA8AA983D738458BD508113BEEF7910BDE9B70F3ECBE984BDF954D53AE2245ABBCDF8BE3C657C80BC79C7A03DC30B5E3D649D973D4DB9E8BC05F54B3DA73C8ABCB949E5BDB8B4A13DE332063D01F203BE3DBB2C3D08A98D3DCC100B3CD2E9F43B45894B3AA0F516BEE01F4CBC57A22F3D74972E3BEA9765BD864AFBBBAFD650BE0BE3003D87A420BD88CC2DBC32FEA2BCA336BCBC0C7E8C3ACF01C83CAE0036BD9DF41E3C0E165D3D9C4ED53D8F20ACBC5A151CBB2B7CB03B2A0C983B81C8BA3C29884D3CCC868A3B00BCFE3CDEB9C53B7E83D6BC729AD2BC41DFA7BBA3DA553BDDBC6A3C13205C3C58006DBDEF4653BDA450BA381BDE6CBC98BEF23ABF1FAA3BFD18873C363C07BC8D5F743B1EE9E3BBB77605BC082D873C511F17BEC9A32ABE1F1AD4BD4294BB38045B723B71CCB43C95D9A7BC6A5BAEBC530DCABB20E3BF3DD5A3A93BC4A485BCB797D3BBA23CE23B1804813C0909C73CF75A05BC8769AB3C2A781EBC9DBF133C82D8113C7F6C43BCCE56183B30497B3CD86234BC17902DBD693DDD3BFE4B87BD551525BCF2AA713E359B333D35052D3CA2258EBC87254BBC422D8A3C4F5C8BBC63E3433C9BCD0C3C4540573BABB0293C070B07BD5393433CF38AFBBB71176ABA1C84333D1858973AB3FDF53A85EE42BD153ECEBB11CA15BC4F31A0BCE2B0133CCCDFBD3A293B2EBD343FD23CBF246E3CAEC2BEBBB2A6AEBC1A8E61BD1353B1BA98396D3B1420ECBB73F5883B639F3BBC4A542DBC5D5A053D4329B23C70F6023B694EAE3C9B1CD5BC18E4C33BBBA0C03B7FC6F93BC87082BC902F983A74575B3CA07DD6BC1FA72FBDD9E7AEBBBEE715BCCBED763C77AC593C7D3C7DBC314EC03DC912A5BBE1AF07BDBF9587BD632A56BCB0A9CF3B8949083DC448913CA0B991BD2B8384BC2EAF1EBD16AA193C4914D83DA587C4BC6545D8BAF0CB243D91BFBA3C0B66EFBC39E22DBCE0ED743CE4822BBDD1623CBCF0278B3C173FCEBC6F491CBCFF8F513B514516BD25BB2C3CEA804E3B7CE0BDBB47353BBD30E49D3C41FBBCBC742762BD4DF79F3CC9072FBC68B3E3BBDF75333C341C28BD6F9D4DBDB294A1BB61FE51BCC59628BB31579DBC43C936BDFEB63ABCE65B7DBB2CC307BD031E83BC86F416BE1723B5BC22B76A3B2D8B03BE97D86DBCCC96413D17DA073D160851BB8B950DBC18DCBFBCF9824BBC83F6093C5DC5A5BBDDAD8A3BBF460B3B044CC4BD71C8C6BB9BDDC0BD278D09BCFE11F93C8BC734BD3A618EBC15A0BC3B547A7A3C1F1F663C4B1CEC3D050BBB3B02439B3B4DAEC0BA528A61BCDA798D3C735032BDCBC487BC1B6BF1BB00C73039C1B9023DA0C159BD505A7EBD9AB842390FB9F43D4C08A83B0C5568BC90D03E3C6E8F193C6A5C5EBCD205093B3E2DE6BC94A1CEBBEFDE2CBCB8FC0EBC7848703C9C9C3BBCC0C6793D2A9DB8BAC42CFBB90C7905BC7FEBE83C7531043A2C7451BBA32AA3BC0B2B273DD11147BCEBDA073EE53138BC93F1BA3CD4A44ABCC9D02A3C6CF207BD812475398FBB3E3BE434303BC2CD0F3C5A4FFABC2A7099BC2437E43A355CCC3BDC286ABC73262DBCB0F0033ED085973B9A750D3C6478C8BB5BE6AEBC5CF83EBC961718BA79E56A3B79FC51BD367F44BCE71A92BBFE83AC3BB77B01BCC68541BC80DEF2BD99E7ED3C5FFF19BCB7BF98395883E13B29D888BDC1B1063CDD2EDBBD8F0AABBCDF27143D9F28253C2A66C83C27DDC23BCF8CC3BCB1CFEABC6B013C3C4D53803D0221A33C906331BD2B88193CA1F2183DCBFC813A1ACB60BDBC2F693C578652BDBE1121BD728656BDC419AEBD1E1114BCA79212BE647024BDD126203C6D604E3ADD68A13B5385113C135E0EBEEF883ABD3E1CA0BD9CBBAD3B657B8DBD0539033D8BB7D1BCC2F42B3E3A7D933CFD76B53CEF6B32B949C749BC4775133A825AB93DDC1B2B3DB404AF3D6C3EAEBD35A8FF3D9EFC7FBAF5949C3B392DE13B6DDF3ABE5FC41E3D0BB830BC50640EBCF4F239BD73DEB83CB5C625BD33FB8DBB0366FB3C7A8730BCEC3357BC36B16C3B699AF4BD893BA23D1AE91EBC9F802E3CF1A7FC3C941143BD034BF23BF00853BD9F25083D5AF913BE5D04BFBDE8317D3B112AAB3A3722623C8771BF3C665D66BC0F74773DDA7348BD6FEC73BD5568ED3D5E1B3CBC09764BBDB440DF3BEBEC673D7F59B23DCDA68ABCE2C49F3B767A8F3C46621FBA03841D3BB2C2853DCCB78F3D68138CBCEA370C3DB4CA793C31A4B73D449E683DE1112B3D096AC53C45EAAE3C29A46D3C8ABDB7BC0E81D13C571FA33D3E0F083DD0DF493DEB1C143DCBCE4DBA329439BCC139233D0C34E23B75E904BE5ECC19BDECD6A33A41353E3CC91ED7BB444E0F3DA9AC2D3DB89FF7BB232B29BCBDA819BD8198E9BCC5B3AB3D69A852BDFF72433D0E985B3C6D6B40BEDF8C05BD1A097C3B2408263CCC3BB9BCB4AF99BDE63315BBC8F8FE3CA10998BD5E2C71BB9CDF31BDE74DA63C98B0F0393674DFBD6A2B473B7F8107BEED5C40BDEE0893BC7D0BB13DBD89AC3B64086A3CD75B8BBC693B113CBBBE1DBE855848BD6AD66A3D73CB423C229A1C3E089049BD064EDD3C0D52203B43351BBD22DF013D199BEE3CB09A8FBD92CD48BC09DAB13CD092FEBD33FDB8BB6CFE6FBCBC129C3C704F4FBE3E6368BD56EC1C3D633EE5BD297651BBD4E8D13DFDFF31BB6B3A983B6B6B92BD0601783CB2DA773D29D80FBCE60EDEBD3474893D1E3498BB667F533B7581753AF5EA613DFFF8EF3CA3005FBDE9B0673CB833EBBCBC45D93B0C3C4B3D59BFC63CD9F6C13AA178D5BC7D73D3BB9F6FE7BBF6FDD53DBD2C6CBCEE2A853AB7CCD1BBD5ADD83CD0F87B3C096E7D3A920DB13CA1F62F3C68A433BDFD02B5BD8E3911BE550809BC299010BD383126BD7F9108BD026B933D5FF4DA3CF62F0F3DB40B953BDC8D803CA4158A3C878619BCF7C55FBDF2538F3C5B3801BD4A9FA1BD3F0BD9BD88E33CBE569840BC514FF8BC8443953CF1E2B5BA76E30ABDA10E40BCA80A42BE4B60293C27FC3EBB915DC6BCEC21ACBD6C7085BD3E97ACBD328BF23C3EF488BDC8997E3D37522ABED535DE3A91E3043EE27E833CFA95803D8E8673BD617E893DA6054C3CCFC9E33DFBD5963C680402BC979CA0BC154D4DBAF1C4E7BD5CA32BBC21B6A63B31580A3D5C125E3CC8F6C7BB9A46DEBDC45312BD198DD7BB2417F53B0B7AE6BA661B57BBD080143DE980EBBC12F87E3D12B25C3C92DB0EBC00F5003DA51055BC0E55763CACFF863BCFF289BC2C4BA13DF0AE4E3BF0F6D2BB74A2633DC7E11DBBB4B7AC3C77084BBC387136BB445E00BB2CB30B3B8F7B94BC952C7CBCEF6B3438FF69FD3CED0ED43B783621BC09B9E2BACED595BC24BB103D46B4303D371DE6BCCBDE4ABADB3D06BCA7A10B3B3F94753CF1C090BC1C8CBE39A9001ABBB5D2F6BB823079BB6EE5033A20715F3BEDF3443D1749B93D0EAC313C2A46D63C2EAB113B85AEB93B911E7ABC0088D5BA5DD25F3C1A89B7BA625921BC176F57BB7C75233C34F30CBCC0C3493D69D0D23CC5B501BDB1D3C9B9326E0E3C0E02973B771929BCB61683BCF4531EBC8493AD3B32E2E83B64435D3C8FBB9B3CD75A58BB0FA406BC9C5C98B7747FDBBC5C2ACE3BCD169CBCB9EEB4BC8D47A03CF51FA73B24CC06BDF56BEF3C738CB2BC37E225373D04F03BA01765BC9AFF163D33FAA9BBD3B8B63B46343FBC4E2A233B5E0DC83BE919FD3A6F113C3CD21902BCEFAF00BC53FD0DBD5F49913BDA7192BAE398993CC07829BC87AEFFBB6CA62BBBECD72D3AD2C9C03B5FF206BB9E440CBB58960FBD4E742FBCA49745BD1F20C8BB04D5BE3C4DC02ABBB05A1E3C4F6D153D24CE5A3CC6DEDBBCCB452EBCF179EB3BC62BA3BC8A73343B26F2003D9F9B1DBC69395D3B3D96443EA07810BCECB19ABC7071053A6EA65DBC992BC2BCF34D593B92D4BCBCBB4D393D26B120BBA84000BAFC6F543BA164493C595C62BC8B13843BABA3B7BCBEA7E5BC86BF3EBA7DF582BB15615DBD04C66DBC53C3F63B1D3CBB3B9B19B5BB5D998BBC2422CBBC20B1993C79B951BDE907C23C8798A83C5EB2B73C94AED03B2A978FBB2E21F13BD47854BC4970A3BC842647BC5207313B00EF273B896FBABC668C2BBC902B8C3BDF7885BC2EC5DF3C6F89A63C0DF739BC4AB6F93BE56C293C4E1A58BA8607AABCAB2361BC84D386BC01DE10BC4169AF3B70520CBCEAD762BCC819AA3A19390EBC82B56C3C12EB85BC707E0B3C813139BD991D28BCEF65BEBC460070BB987DEB3C41E7D03C31CB6DBAF06C22BB32C76B3C889AA2BB034880BCAE010DBC8F36943C8EBD773CDAB60DBCC46FA83D860F093CF4B929BB00BFDC3CCF43BDBCDF5A75BC593CC0BB9DB74CBBE613CABB58F2CCBC9E44513CE685AABB628B033BA7CC47BB4391B83CF1391FBCBD4ED33BA664AB3BD723C7BB5836993B368AF9BB3D1083BBA805E1BB35E1103CC3FE46BCA47FDD3B5098133D024E793CEEC2D4BAE4438FBC0E5224BC66350E3CF8CB05BC8E1CC13CF6AE19BD9E1335BBC4F7DBBC588194BBFDF9B5BB077E7C3C049EAE3C461BD03C198C023EC1B2A33A2E76F6BC57BAB2BC7FD6B43BFEC0943D4B3D0DBDA5F338BD5B1B09BC772727BD887B803CEDD6C9BB0647A1BB2A5D333B64F75FBD13DCEDBD757CDA3D6E419FBCC1E30A3C085B2D3A1663903AA56710BD5E15213DDFD49E3D4FA0D8BCC710153D2B6A443BC671F63DD066A2BDAD8898BD4C2F5A3CF706873CFC5A223CFAF1973DD4A549BCE6EAAE3D2448673C32CEAF3D53ADE5BB229DA83CABB40ABDF397453D5D10CDBD4520C83CB64DDFBB39E317BAD994533D98151C3DB9F1C23DF030CABBDE4508BD3CD13A3D25FE513CCC67A4BB3F608C3D901B36BEE75445BDCFC09E3D591840BD40368A3BDD9983BE606FA93B86456F3C8967CFBBB443DC3D0A76073BF70453BD83AA29BC0C682B3C7187FDBC0C0D183D0023AF3C00CD1ABD1A72DDBCAADABF3B5220743DFAD98F3D5BFFE9BC8E929F3D905A37BCF410CF3C642243BDF0907DBEE22BB4BCF0BA81BD1A02C33BF286D2BAC66633BD34F8B5BCA56E673D693C6D3C3A4C34BCE572173D128986BD9F1F8B3DFD80B4BB0244B2BC19DA50BB2AD291BD1BE03E3C211C533D2F12D2BC6B1383BC347FB43C88502C3D3C218E3CF1B30C3D04116EBCFC14323D7AB821BC7CB5D93BA251933C75C22EBABFF7673C96A040BB55CCE2BD4379EB3A384466BDEE3B513D4FB6A13CA576033D23722A3D8ACD023C3FCFF53D8DF249BEA6268FBB2BA060BDDAD23B3D3D7ADB3CB1000F3CB2FCA93BED933FBD31EFD43DD1401DBBF331323D38BB943D93FF523DD7571CBED0EB1ABD6F7C1FBE6117253D566B673CAD0459BD1FAD95BCD35B733B826CF2BC10FCE9BB5787793D972A8F3DE33733BCCFF31CBE8ADCDDBC2968ADBD59AF73BDFB193D3BB8C1073E7F416B3A9CC150BDA64BEBBDFDB73F3D64C745BD483D233D65C65DBB85FD3F3DD3CA8E3D3E7FC4BDDA39853D458B133D364B56BD0FA8083CEDF6B6BA638FDF3A81C222BD582F1FBDE2D1623DC93C5DBD23288F3D9473CF3C038A95BD8C9A92BCA21C5FBAAA289FBD9441B4BD5DF9753D49DA053C4153E63DD92B4EBDF71327BC3E08BEBC40E5EE3BAACCFD3C60EEB4BC432C8C39761FAABC5D86793C808009BE9B3846BCA95C883C2AC7323CA56105BD40976D3CF3700EBBBFAD173D875F4F3D3BAF583B38ACD7BC846331BD9888C8BBBD3D60BC756149BCD465B23D7EEAE63D47926ABC24A1AB3DD814F63BE3EB3D3D990C1BBE18F6333DC31DC33DE53085BD2337023EF00AA0BA2E12833ABBF737BC47ACE33B60D3953DAD3B8F3CD4D52EBCE6430D3E9770E53DA69A373DA66B033D96AED03DFA71673D89D515BC9948D53C20F7883DE304D63D4E84AFBC7563A9BB74AE43BCEFA3E33C5F96833DCDA7903DC0339EB91EF5653CD6A4A0BD5239BE3D1E1D843C332147BDDB9FF83CE310DA3C446AD1BC1BD40DBE826445BC2EC286BDA5252DBCAB932F3D6A33E2BDCA4D203C5EE9A03D4B66B83B1EEB963CCAB167BDCB86F1BD197DC0BCE691353CF8E4233DFF16193DBF7D19BC9CE2983C51FF663BBE08B7BBEC4D853C603E393DDF3C6FBE98FD573B87D23ABCA3CD373C0DF2153A51E92B3B371D20BD3088803E6CFA26BBCDB300BDCB52123D5C92513EFB6BD9BCB2D41FBF8AC6D73A1D19343BC33B9E3BDBA3473C6C8153383683A9BCDCDECA3E45CE0C3D94F48BBCDD153FBC5ABE4E3DDDB84C3DAC5CF83D0D22803B8F774D3C5D78723C9B6F71BBEBA836BC04A0EABB276D993A9189C93B868CD6BC228449BC024A1E3CF9E0F4BCDEAE3B3D872D73BBC0C2C0BC6497E83CDD22D2BACD72A3BC89C46DBC75C2D7BCBA1A68BC0CEA96BB65F0F8BBB6B84D3B6E26AD3C26590ABDC9A3453D2D3A15BD60F6083DB35C663C4263CEBC433B23BEBED43CBC46D3F6BB2FAF75BCC0DC80BC76640ABBC7F4FE3B8C7019BEF205BDBBFB82A1BC5CE72F3CD8F28EBE4BC203BC2B3924BCF65BEDBC38FBF33B179F7ABB418EB03C785275BD5780533C8948C6B86BBAC83C190C42BD1A270F3D7A4BA03B54D58A3CED03CE3B3AC78C3B03F2033D4B70F73A17A5853BFBB1643BA0CDFABCE03300BD513C61BC64F0583B3EC1103CE9FBBBBB723E103C0FF735BCC5F125BC93790CBBD8D87EBDAF61A7BB4C387CBE3E36BC3CE424943C4108C8BC8B2C8DBD8AB0933BC6FC52BE793F0CBBA46F2B3DEDEA78BC64499DBC185662BA5764383D059BDE3C5657153D07BD58BC60209DBB2888923CD56BA63B09D0303B2C575039612A7CBD1C0C4ABE50C2093C994416BDE4A4F2BD19C7F9BC61E426BD2B1E943A62B3BD3C3CE885BD41F1E43BA8C7143C615CBC3B8156BDBCEAA9A93C999C893C8CE7B93BFFF23A3C3F6809BDA91F16BDA42759BED2B872BD584DA3BB4365BF3ED51AC63CA519DBBEF4AFD7BE15A3C2BC304C323CE8C851BCF1DF36BDD41E9BBC73BE6BBC1757F53AB0D990BCEEBFF3BDF99092BCE730AEBE203347BC97AB913C0A468CBB7239D03911B06EBCBF091F3C95C4D03B79EDA93C18440DBED217CF3BF4CF9E3BAA5814BE9266A1B9789A5CBE7806143D5FCDA9BCB4A9E9BB646DAD3C5E61C23E37AE003DFAF711BD212C2CBD345CB93B8657D6BD5E2501BD47382ABCE6549A3CF89DD2BA1F76B3BC592B5BBC4B8FC6BC651922BB110F56BC5E8E10BC70C68F3DB92AAC3CBBC8D33B1CCB37BC42C828BD9FFBEBBB7044973C752D7B3C555EE3BDF1D9E6BCEA85EEBD3876B8BCC2AE52BB2DC7803CB5ECE33C62773CBDBF48D7BB771D273D46CA2A3C8F30AF3CA5E1B5BCF7A701BDC224873B3DDB9B3CB7D0973C2B486CBD0941FF3DA84C83BB35BFC4BC940CD4BC1B20EDBC537000BC539ED1BB5F9535BC50C80ABD9BFE433D9027D9BB0C9C353C3F4354BC3DC7BFBBE1CCDABEEB472F3B08E4583B533144BC352431BD20749BBE75B8A23C8ADEEDBD007669BE7A80433EB336C03C1A7A873D6603D9BB4B883CBB541A96BBB55DD0BCB32988BEF5AC12BDB0D8B4BDB3910ABDA4D74A3D539D03BDAC74AF3C8F5B3CBC09F234BD75850B3B5FBB9FBCE7A9C5BD09FEFCBB063A97BC9FD57C3D938C44BC0C4BEB3C410DFE3CEBB4AABAEE2A12BD57169DBC539B3B3B5B7E07BDD57051BDF49577BC51952E3DBC1D173D8C913E3DC2C2FCBD157C9F3C25811C3CE87EA43C21C1643D52F9AF3D3C5C953C650F99BC5C44D33DD83410BDEC8FC43D69FF6ABCFE2EBEBCEC813A3CA6F2C9BD83A9B03CB68687BD26A6BAB6511ABBBC042C1DBD044271BCA1E2E03B5B3EAC3C3417273A51BCBC3C1F12D43D08E757BC16C41EBE336F95BD4EF4B33CC544673D3F55DCBC84FC72BCE82AB5BCF5DE83BDEB9BDEBBD2178DBDF340003C33EFFA3998D6ADBDB172BDBD2E7526BD5E7AFF3C6110BA3D717D59BDBBE1A4BD67DA14BD53824CBB67D703BC4F025B3CECC7653DFCF349BC09FD0EBD1074B1BBE070CCBD99F6EA3B4A80F6BD8F920ABC4C37F1BAAC73DDBC6842BBBDF541493DB8B324BD2F4EABBDDFA860BC61403BBD8DBCBFBC528AC83CCDB7F23C53EF3BBCBF01213D276E383C377B9ABCE24105BDAF3B12BC9B91513E6C1ADCBC462D4E3B08FEBC3CC8D4D03CD587343CB630563CE434DB3CAC5D89BC712C6F3DA9B08E3D0C3154BC0A77B4BCDD6847BD6F9DA2BBE012F8BDD625513CE6D78C3C5B8DB53DF10CF3BC0B4E55BD6B45803C48AEBFBCB18CF1BCB1AE233BBD03173CE0E389BB994ED03BA2555FBC01C6743C6C9D52BD9993213D672B833B832E5DBBA73FB7BA43A4B3BC39A2B63D02F2733CE7589DBD58F533BC1D8AF4BB978CC43DF3B4023EA66540BDF4D366BDAA32803CF8F4B0BC8A54903C0AB906BC1F01A9BDEE0FFCBC4901A83C75939DBDB21B4ABE92A3D23CA9E0833C0AE751BE9602FFBC311DC1BD4D876FBD1409BF3CB43C5BBCFC52363C0A10F43C35FC403B7FACD73CD6C801BEAEB1E33B94C1673DA5F181BDED4334BC072B2CBDE8DB7DBD2A099CBC51072E3C7C0E51BD7FBBA23C76EE7CBD9C1902BC3ECA22BD1B0E873C5EF8F83BD6623ABAF41BB93C653A38BB531DEABD4AEBCF3DA3FB65BBDBFA4CBD36E82A3DF86EA43C3BE96ABD5A2314BDCEBBF63C33F558BD6AE27ABCA76BDABC4F1C09BD0C78EEBDF2646B3D3AA3E7394E2F2D3DF1B91DBD416964BD1ECFA13C0895F3BC90D1013DEF4D703CF85D22BD40AA0BBDD0F9D1BD4A891A3DA73631BDBE5631BDD2400B3BE49BDCBB3971CDBD58AD8BBAB7A6913B118716BCBAA5D1BD08A8FEBDE16F11BBBB3D233ACB7C17BC1DFF20BD1EA5DFBD9A2FC83C179B67BD4C32863C595631BD71E8AC3B3A34E7BD3F43BE39C51923BD2146A8BD223817BE6A9B44BD6C1F12BD8601A93C21A1CB3DC924F2BC1090F63B089015BDE78E6F3BF36D143B7EC38FBC2E0A29BE116018BDBD4380BD7CC433BDBAE6C63CD19A3ABC6B755DBC8907593D84FAF33A71683C3C38E27D3DFAFB5DBABA6E1C3D503B203CFD87E3BB49D853BD879A0CBC5D7D253DC584FBBBA0EDF2391D71003D4AFFE23D372A373C4FF79DB95DF58D395789B23A5522003DBDEE17BDD80F50BB97B92A3DFB0E013D701BCDBB797B25BC2D2FA9B9F0A57FBC2A702EBD18BD2C3B57ED8A3B11B3D3BD15B801BE2FA597BC985DFFBCF90051BD06525FBD94CC233EA307A43BED8739BD60A81A3D43EFCE3C6F1F763CB4504D3D2A15253CDD34143D4374B2BBA73222BD110108BC85B339BE9D7499BC0020AABC2DA5B1BD15DFCFBCA7A5F43B9FA494BC2B642ABD2895403D6A0AD43B78B8603BE54617BDEDC21D3DA5A8C139597B32BEA9EA523D46B3F2BC11F969BD798E33BD52661EBC703E083B6F73853CA5A847BD996E993A1C597EBCE07119BA9A9269BD983A96BCA1CCE73ADCFAEFBC79CA2FBD8CEFD93B854592BD75E2A7BCB9F6AB3DA6CDE5BB60474ABCDAC655BD37E228BC8E76533CF15768BD9F2664BE960D12BDB70E713C522100BB9BEBBCBDA1AD66BDEB685ABD796806BE05BB72BCAB1EE13B31110ABB7F7FE6BBDE47C73CDFC5A1BC61DD6C3C50F5C2BDFB73B93A753874BBDAB276BCF73CFD3CD34A493CADD50E3DE29672BC80BB5FBDC894903D514C493D35A888BA94796B3B7A457EBC71651EBE568DCC3CBE3825BD859E963CD3393FBC71B3C8BCA6FB9CBD8A9709BD209E69BC263D0A3B039A9BBC0A0D05BC0079913B6D99B7BCC7159B3D27B6233D531982BC0EEFEE3A3AD633BDF5812F3B7AB7F13B8C936FBB6E94DABCF372B2BB150AAFBA4101683DC625873CC2290EBD07B5BC3BF94C013DB7B72ABE27E433BC9DA815BDAF60373CEE5FFE3B9351643D208A6ABEB91E283BAB7AD5BC935AB03CBC7352BC200C293D118890BDEA584C3C61A57ABDC2443F3BE69D0DBC25AC64BA009A8FBDF1978C3C902A273AADC2DC3C7B21F63BE86F053C5FC0553AD41271BBD44021BEE7A091BC6BC7BEBCB97FC13AB23AE1BBFEC289BB84CB68BD498FDEBD42E4A9BA196A8C3C67A5133CD136C0BA85A7FA3C0DC4473DDA61FEBBC13BBB3BFF8952BE72CB96BD14E5FFB90370863A46CA1ABDC3940C3D8A72AA3D38F80B3D1E332CBCA7477FBCEEBBBEBC0BE48C3D50F9C2BDA524FC3C66D984BCD410AFBB6FAF4ABC5BB29C3CED8E163C5B8427BDA7FE44BE198C8DBD46D654BD69813ABDB54C4E3D834AC73BD57552BE52D6363D2BC9AC3CE6991EBD553EEDBCB77680BD3B4D30BD30EC82BCA40EE3BA88D61CBCD4FFBCBD8EA3B43BCC33EF3BA1273A3C7D7D55BDBE9C89BD8DD585BD5CF9DDBCE874943DCE8638BDF112153D172E1B3D94658BBC1592BAB9530ECBB965826DBB8BF83D3DFF37CA3B290FA5BCE43D923BA2807EBBE944213C1EFC193D791992BC3064793DFB25D53A4639803CF82119BD195ED7BD164A00BE8474233DF81FFFBC77A30F3DCA48533B440324BAE954183B790AB4BD211E57BCB8C7D03B2FC048BCB029283BF554C4BCC7E634BD7646953C84C7F9BB8D2E80BC8DF8683A82C420BCAFE7EAB9208F30BD979D73BDF7E07CBCFFF7D0BB31C98EBC947E79BD2338053D5A54AB3C710E08BDCBEF4CBC39AF12BBA582053D4FA6E1BC61C419BE76F3EA3C37AF0B3D11CD8A3DA40BF4BD5DAEB2BC40055DBDD3F6343D6AA23EBD808D703DB9EC39BDD0B3043C0D5310BD4D764BBCBE19A0BC8307333CFB4762BDFCA91CBDB50BD53B6916453C20F42E3B80E01BBDE68727BD7AE927BD3CD7633DCB5B67BBF5CD843CCC79A4B9D0CCF0BC7B0C24BC6751FABCC27860BCB4DD18BD747B8B3DEB9A453D44EBF2BC1A4BD73C34D2BB3DB3C80CBAC4DD76BDD3A8F7BA1CCC4EBD4FDB5DBC64E5D53BE64F3FBD0A42433D9EEA1BBE9CFCB03B51A8243DF7ADAABCF11594BDBA04BB3C5D7359BB0A7B673D9B27A1BC61072B3CD1DC1ABEA118143E5EF9DEBD5C57C83B7491E6BCD53B1BBD72F78ABCFC3A913C1175F4BC4C110BBC27F44AB891A3143B78CBD53C7C2F10BD60076D3CCAA2D93CDE3F43BD3F3B433CAA88BBBC7ECF26BD8BA4A03C27D1ED3B954A53BD914F76BAED33D3BC46E4B23C6D6525BCB67A443D3752C2BDF55B363D9DFF09BE314D8E3B8E2ECABC403A1EBD9023FB3A50FE403D868A9ABDEBCDA53ACA60813DDD773BBB127611BD480A51BD4458C0BAB26FF8BD12870B3B9156BCBCF38DEF3BC85FA7BC7ED8703DE3FD2F3E2C86CFBCD8F0D03D8B46033C0C92D3BC8502953D2AF232BECB6D11BD1B36813D7228B03C6CCE893C10A51ABC4586A13C71D72DBEE4249B3C9E2BD1BB27CE1DBDCFF8363EBDF72ABB6C8C5EBDDBC3E1BD653A85BDB6AEA9BD8E90CBBD7E47913C8CEFEE3D02C16F3A051A42BD5DFF1EBB3A7B9C3BA960C9BA0E6E163A7FB5E5BB448578BD2AF7313C8B2F0ABC881A0F3DB89299BC95CD6DBCC8EAF43BFDCDEF3AC864F3BC189D9C3C8B3520BDF97BBCBDAC61CB3B0B9256BCF2ECB7BC56A217BBA3B577BD40721E3C524D85BCC87EE0BB375DA4BA1E6E73BC065E173C1740263D614D323D12D7CB3CF8AF88BD515307BDFF56933C0EC823BE54B131BD866D8F3B9F9EB23DFA55D8BCBA9448BCB34ED63B2CD2E6BA7AD2AA3A981D563CEEBF4CBDDE1D80BDDC6A28BE341C6FBC42EDCEBD3B0F10BE1A935BBDFCE3B0BDBEE2E83C229E58BAAF9C533BD71AD83CBE28013E5FBCA6BCE4C53E3C2EC4523BB07657BDE205BDBD06F820BECD58B0BC47FC0C3D95CA0D3C350506B9C076D2BB7E3C253D6D09383CD53D203DC272BABC22718EBD4A18F93AD28AEFBC52FE89BC40AC3EBD0A448DBCED8785BB884F0EBE49E9223C9B539B3A6568B33BCDF6C8BC188763BC5C1538BE7E17C93BE4776DBD2AD654BD0B45FA3D852A13BE38B9DB3CB81B98BD5C68803C979CB6BC982674BA17C5343ACD232CBD957766BD8C4DBD3BA6861FBD158C413BB0C10D3D1CFF8BBC54EF45BCFE669F3B7E2D7F3CD58D0ABC021A213D216A11BAACBD923B0A2982BDE3D881BCBA60E1BCBCF7213CB201853D75DDA73CF88B4EBDDE4552BD8027843C2C7C4C3C1CA1EBBD02446FBD58EC37BEC42E06BDF7AB2EBED944273D6FD4BCBD2817BDBC2219FF3C4D6E483D81EC0DBDF23F283D9B868CBCE3B8073A6FE6353C98AD15BDBAD4A2BCB77EA93A1C5C87BD3DC618BD3409013D8155DABC130A84BC8A5DAABD02B1923BDED045BD2FC9FBBB9807A83C73F6A63C2B8C213D613828BD9DFB9BBB80105D3D8271F33ABE0F41BD6079CC3A30F343BB2AA7003DE22A40BC26A058BE399AB43C1C288E3D292E00BD71A98D3C598DF9BD2C4BBFBBE679A2BD12890DBCCD988DBAECA2353B9CC0263D6106CABD5D2CFABD2B9C37BD5CFF1A3DA563B3BC63C590BC05A9F03AD2E092BDBDF1A63D18FB49BDDE42853DB5692CBC738912BDA3953FBB8C6C66BD4EDCB5BB9E8FF43BE724AA3C9E6BD23C86996DBC321711BE8DE54B3D1B43873C2EA7BDBD05C285B96FE5D6BCF1618FBBB275933C46030FBB0406CEBC7CF57D3CCEF062BC90FF753C3AF59B3BA530FEBC3EEE46BDE4CC1B3DBD7DFCBD4D22113C66A816BCB4A3E4BC4FDC623BD9865A3C29F3C9BCF25B06BD6688F83BB3259B3A9C6197BC67BD8EBC52A3783C74A24C3DE8D9F3BD1454A9BCE1CAE03B90CA9F3C68490A3D930033BB6ECA88BA9FE9CDBD40E9243D3C5DC53CB2CB2EBE8546F13DF013263D279973BD4527DABB8C54A2BB3911A7BB274656BCF959203E93E19E3C59215EBC295B13BD7F0BC83DBC590D3B7D77BC3CD4BACBBC5AC3BE3C9B700ABC540DA93CA10EF93BAC049BBD710759BA0A90EDBC3A6A983D8B18353BC577173D7E8D28BCC2A3EABCA8C6E43CA490733C335D8BBC3822B9BC402196BC9C8C25BCCF66B63C94FD2DBBD757223D33F595BB1CBA6EBC5C748CBD6A54C3BCC0C91DBCE12885BBAEB0A33B91AF1FBDC638A83BE3691C3C9BD895BD6CCD58BDF69A7DBD312C19BCF297CD3CD41D803CCDA53FBDF79E02BD9396C2BB4D74233B06A44D3D4ACDC8BCF86A0F3D70CE5A3DA4C00FBC7B77183D3728A43DB35EA5BCCD16983C4589913C826B6A3D8FFDCB3B16A4593D1F635CBCF0D6F93C0D2625BC5FDF753DEB5F34BD3A5D16BD2A412BBDC5B94D3C6AF88DBD070C4ABE386AA13CD3C3C63BDEE0A3B990D559BCA05445BBD010CF3B10C2D0BB862E753D3AFE71BD09786CBCC00B80BDC6BF9D3C0D9DA5BCC28008BED756E3BC39216B3BB471DC3B84DF46BDE879403B7CF981BD2B6F99BC7135DCBB92CA0FBD01AF8A3C22F1323CB08271BB676AD3BDAA372DBDC8D0883DD5845D3CC3E234BDC05773BD149D4CBC07EA61BB90D7053D6651973B0BBFC23D3254FC3C56312CBDED0F003DAE62D0BC1F6F113C55AD8C3C51D071BD97CE2A3DA197A7BCEB2D593CEAA8D53DD71AFDBC4132E63CF32A063DFB5D9DBC3C9902BDC23B31BC6CE742BCE82C11BD2105F8BA32E72B3D84332A3DAB0E35BD158A88BC393A78BCE460193D5B5B103DA261F8BCFDB0083DFBD51ABBDEE8C4BCC0377D3DF95003BD110A26BDD37DB7BA41870DBDB41CEFBAE70B613C261A35BDEDF8333DCB770F3D4020253CBB82963C6E3C6DBD0AC6693C74B621BD6913B2BCF760FABC11561F3DA3F2893B65C9613C88B41ABBD020833BA94F093D25964E3C29158FBCEBFC69BDDD1987BB448D243D9A8406BD821F3FBCABA6D53B3BC713BD2C606DBC721809BDDF8995BC986C9CBBCED1A43CE77421BDEF86723C33CAE3BC1C3811BDF44347BD5675083D67CC96BC563B09BC1D90E9BC257AF83A0C696D3C5AC1293D902510BD8005773CFADD0C3D12FCC93C3FA486BB52B9B1BCEFA98EBD6B1B0B3B449D883D2AC74B3D8164D2BC146D95BD22AB92BC675071BC55EB3C3A7E5A5ABCB8D9B83C01792E3D9277EBBCC091FEBB4FB191BC0D8A9B3AECBC9DBCE0B8ADBC2A3942BD0C1951BD7DC5B1BB62D11A3C83868F3D95329E3D135DF2BC4D0353BD5DDB5ABDBB1907BC65B99BBDAE3F9A3C8CA2093DE125B7BCC867CCBCAFEFEF3DBEF98CBB4C3F64BBFBCC133DE09DD2BCD203CBBC2E018F3C45541DBD9128D0BC90064ABD647A0C3DFC1B383B2B592A3C5C6EBABD73D9873DA37771BD544A6DBD51DC4EBDAECD5FBC9F414EBDA8A8B63DBBE4BE3C691154BCF3032BBC8B90253A101FE3BC241C53BD540B9E3DD72CD0BCF47BA3BDAFAFAA3D0FAED03B0E1607BC739F6C3C851DBEBC9D9EE2BC5A9ED5BCEF7B77BA3FCA4E3AA299B9BC97212B3DCF93803C2C89C63BDEF4393DB0A08D3D3A8C62BD07CD203D391825BD5687BE3A5C24D23C650D09BDC19BF3BCE2CD963C5911843CECF716BD1634B13C87591C3C633990BD86F09C3B1BE328BCBEA7B13BD1658C3CA1C7513B215D453A1D00753D9CB0603D8634933B43E987BC0551FD3A6573CC3C730EFCBB2B3391BD200F6DBC6A0ACE3CE16354BC4E3E57BC6812E8BA23DAAB3B95E50A3D0B550C3D926153BC4D63DCBCB514A43C0B4826BC9FEEC7BB788B0ABD64A1643DE6DBE0BCD051173C36B47EBDF644573C042A97BD70BCB3BCDA92EEBCE435B43969BD6ABCD96DDE3B781604BDCE3FE3BC49336DBDF21C77BCE0191F3C13141E3D657077BBBA3F30BD9CDB77BD17975BBDC6AB3ABCA5F4A03B8AB611BDF9EC9CBD46B999BB45EE183D4B950EBCE17A08BDFA365ABC62B804BD26387B3D763A873DB00A103D696619BD9F4AA73CC5D117BD5A59893CAC17113DF7B9F7BB61F80E3D2E760DBDFCA7C63BAB250CBDA388C43C3F2F6EB9F8D06538A5A7E3BB9773C13C12E679BC5610823D51F45CB98EC1FCBB8D8F453D8FA451BCF156503C8281BFBC6A6104BD954387BDC67F843CD5799C3CC1FEBE3DDE872EBCCF06893DB5D2123BDEA2D53C4DBEDABBCC8C1B3B75B8C7BC138BABBC3A41B83CC8583D3CCB2518BCE2F73ABCC50EDEBB595B20BC41F4833D41B70E3CE26533BCC574743A2826233DA89D02BC6C38D23CE2B1643B5F1ABE3B8ED4BE3B2BE1803A4110DCBCA226F7BBB6C893BA29F8D23C471A99BBD4703E3D8D1CB93C0DFED2BB6A2DFC3B0C70CFBB681802BC6FB3083D275EDFBB9E5901BB324B8ABC3B0462BC2FE0E2BB96853F3C10EB853CC1944EBDB2BDC0BB9D4C3B3B2A068F3D17639EBA05DE0DBDF106E2BB99FD9D3B4FE3A33CBAA18DBC481B3D3C29AAAFBC8F5F40BCFDFC193A1F4F653B6A6E4F3CC4A18EBC48BB5FBC5CB3863C2C7108BC1402E2BC4CF5513DC66E973CB5399FBA9977073C3EC890BC211082BA334D94BCBA34053C8490C9BC3801903B7B05503CDA899DBB413EBEBBED3AB9BCB194A3BB572409BD0464AC3CA313B1BA65C95FBCB1A43FBCEB77FEBCC533353DD29A1E3C3E4EB63C76EBF83B5ADFFF39D60FDABCCCCD3FBC320EEABCE42AA6BCFDC1D33C5AEE8ABB547A9A3DFD4C073DA8824C3D650FC0BC44EA6DBCBF6E0F3CF52E09BD5C7E03BB13E3053DC85D93BCF6E1943CC7D7333EE58380BCB983CFBC406D163C976FC1BC675BE6BC986D533B0CE67CBC00B25BBC15711EBC34073BBB1CA1353B2BCC1E3C004EADBC43201A3CB79124BD449719BDCBE9A2BB8537A9BBED2080BC1B5BB8BBB572AE3CF155D73C24C433BB6ED6C93B3BA9DFBA8324A03C9E8793BC1E9C773CC98DBA3C5362093D409F973CF8812CB7F293623C5B42E13AB36FC8BCF1BCD6BC6965EA3AD406CA3BAE87ECBC6C7516B99B0E543D96C082BC53550A3DC22F743DBF951DBCA7F9453C1EC5813C6D9729BC11350B3D36F51D3D1E38A6BCA2F34BBB17819D3A30EE8DBCF0A806BD50966E3C3B091EBC763D703CD735A9BBD4B5713DC137BABCAF829EBC8C85613DD3AFBABCF14C99BDB265B43C528DB73AD9F152BC76FE8C3C75A279BC8C0AA9BC505383BC85301C3C42DA823CCBEEDDBBE538EABB24B3DC3CB893AD3C1274E13CBC4FD8BC14CF66BC178D013D440C8F3BB27697381F3E5BBC2371183D60498FBA5E9C8E3C3E8FFB3B2A682D3D35CC95BCC7BCB2BBD693B5BA095919BB5EBB2D3CD2F8173BF1201DBCDFCE59BCDDD4BE3B829A5ABC38F22D3D38DFEA3B62D7493CE2442BBC05BD39BC8D1FB9BCDB19E73B448EF83AE1D7F73C8D44B4BC19F9A53A63D909BCF52C0EBAD996773C0DE5333C1C7201BBA6C2AB3C9AF7103EFC531ABD61E1B5BCCBB5CA3CB165B63C737D90BC20F9E53BE0916C3C1553B8BC702A75BBC3C7D13C2F4687BCB10C92BC89DB24BC4DC3563D4158003DA3321FBE11E238BD59DAA93C720E35BC7348E03BB9305CBCDCE495BAF7B34FBDBF809ABCF49F6ABEA39F06BCB03C423C8513403D4693BFBD67A6B23B6DABA8BCE6EA4CBBB5D7F43B5DF0F6BCA13CAC3B045B96BD292D1DBD330C153D8B729ABCE79306BEA071F23A2F8115BEAF30F5BCE0F255BC2A1CAFBBE76E9FBCAB31A33CFEB907BD4DE302BDD3593D3DDD2C623BA1998F3D8A66B5BC7D8E95BD5380C0BB904983BCBC7B303D7DAE883D94CE8BBBA0849F3C1EEAAD3C9384163C8872353A6348BE3DC31F6BBD439131BB5525673CF6BD11BD231CFFBCC0F774BC654BE0BD68AC863B7E5E193C154024BDFB2516BDDF6A10BE32397BBAE41B5A3C51B297BCFC7E623C599556BDBA2988BDD9BC173DC5608B3BB62E853DC1AC1939E35F04BEBAC5ECBAAD830BBDA3E1ABBA9741C7BB6B39843A3CE101BD67BC81BDAF0E163AEDB896BC488A76BC56F99CBD2AE99CBD558C853CF39FB7BD7F59D1BC4C1C94BBD03BE3BCDD5E2DBE8895C1BCD56DF73C0B021CBD7440563CEBC02F3CAF6924BD9022023DFA5277BC9D836F3A633E00BD5DEDD9BA1836F63D79A409BD1515933CB825E13C5B9ACBBA23A32EBC2E10FFBCF3AB963B27A0CD3B1084673D05CDA2BB7DA7B3BB128995BCF5C8633BEF0CA9BC8E93C5BDA38FEF3B6A585A3D0C29683D4A7EA13BE1E7D3BCBA6D9E3CAA25123C1AE4193BECDD83BD9AC0EEBA107F07BC70B440BDA440FF3CB33EB33B35888CBC61AB2FBD07005BBB56093CBC6F9309BC3ADA433CE733873D5070F3BC16DE423BAF4AE5BC9E61A13B9B500C3DA0007FBC75B5A83C2B1277BD12A7FAB9C03AAEBCBE5CE63C25F9D63ACC62D8BCC8F9A33C7A4DABBB6F271DBE119276BED00FFBBB8B8B2DBDFB8BE6BD03CD73BD009C36BC217601BD78E8B3B66D2D99BD11E08DBC86BFFCBC3E68923C9234C33DA68C273D53F5463B1BF68ABC963FA83CF75B183C4E7B45BC82670CBD1E4800BB2A5874BC584CABBC3C5E96BCCA6B283B2EB5183D584FE7BC6B8776BCE91FD0BC83D8B03CB925AF3AABFBDD3B983153BE066795BC89A80539769D09BD70297A3C13BB833C0689633C5007ABBB543550BC2A3D83BE111E303EC8F15D3D80DD193C35734CBD8F514C3EDDE526BD9F66C5BD6925763C9E9D0DBD2A381EBC28B6ACBB738FAEBBB7F1273B189D87BDA3FDF83BB9E858BDA4B487BD4B9E113CE3C912BDCEAA1D3DF91E4FBCD55F883CA63582BC2027113C8E46F8BCE93696BD300189BD9A5E6A3C9103CFBBF362A13CC4C091BA6E4E02BDEC489BBD97FF4B3C2D8A35BC7F1D173DDDED333D0BB156BD1669A4BC7F3F89BDD9C99ABC7A2F28BD46731CBCCE7A213C2ED7933A7EE095BDAD13BE3C78EEB63BA467563D7C5FBE3A49FB28BC8DAA973B88D302BE21D13CBC3686E9BCBD240ABD369353BCA46D2A3AC352BB3C83C1A33C2C07AABCB20D7A3C57B18B3D0A724CBC692383B893D3BF3B43E061BB3B4E3E3AEE22D03C2A35363D1A239F3E78B29D3CD26172BD429ECBBDD83C0FBDBA9BE2BDC9FE0CBEFFAE05BDEEEDEE3C0DC20FBC69FCC43C4E73E3BB4D814ABB923EC2BDE36B583D02B696BC67D13E3CBEF5193DCC42D5BB16CA45BE9137673D6DF21C3C5B77833DE6A724BCF0806BBCE4733ABC6781CCBBD2F409BBA09514BC1F3035BC71D46F3B912CD53DD939C1BCA5D3B5BEBA9D92BD844CA4BB25817E3D74337CBCC520933C7381C03C380D863CA285E7BB9645AB3B79341FBC160E78BDCAA449BCC86CE83C15E007BCD2CCE0BAD2F51DBB1BC1033D592C0CBE24D9983C8CEE95BCE3D085B8B86E13BD04F8553B692C4B3C4BD53BBE8E18EE3C1EF7D93CF566B63BDC0CF83D0DC3D33B62DE0F3C427F4B3BE6FA4ABDE791C7BC4E9FB03DCB2BCC3D2D3AFCBDDC19BBBB1E05C1BCBB0C403CC416773C0685EF3C0E911CBA48C467BCC862283C9AD5163D509BAE3C561A303CFA6F8A3C0044E53BC62020BDF71F01BC4E96DEBC1D1EB9BD9E22B1BB6C5573BC2B633ABD088D2E3C65820D3BF0E5FD3C6F24143C945360BE93814D3CDAEB393EF0A1B73AB4DBECBD32041CBC6116CDBDAD6E373CA276213C588F5BBB50DFA3BC4F701E3C6355DB3DF112193D5F2D3C3DFEDDAE3B921854BCAF47CFBCFB19D8BC63ED373B3C20743DF773ECBC712B35BE35B96C3DFA8702BD91B0413E1DB2CB3B0C2B8DBC7867F63B4F62533D9064FFBD31F672BC00564B3C6F029B3AC8E806BB6A092D3DE826D13D3CFA4ABDDAEC4BBB013F003C2319FD3C2D44953D174E503C783B863B698CD43EA67532BCFE3EB9BE97B400BF846AD53BCFD5BE3C5027E9BB4BD26DBD4045513A05422CBCC047FEB97263EABA5CF9FA3C1C263F3B60663EBEA0AA2ABBAC36E7BDA0C419BCFF490D3DDC9435BC35B0273C3846093C64C5BF3CCC08833E42EDB8B96B30F73C560246BE287930BC95CE63BEBA519BBCA40069BCEAE910BCF238B23C52FBC63E37B7B83DF61DDEBC904CA93B54C9E23CA0E510BD1DD100BDE94FD3BC01C6BBBBAB57693CDB960CBCDAF4F43B00E6613CA5B3843C792F4DBCF9698CBC303037BBBFD9DDBCB4ED8DBCC30C113E5DDD80BD70F0D4BAFAD9B3BC6A031D3D15E2013EDDD971BC0077C8BE7007A23C82234F3BBB1DDDBC2CCA203D8FA29B3D0FD78ABC6883123DD971533CD5FA803B96C5613C4A0E4BBCAF5052BC0604B03C1584A2BCF4BEF23CE4DC1BBE6DC019BC380F303C44ECB13B6DCF633D6579353DD011B5BC8D0864BC83C14CBCD4AD423D97732EBC8275833CB61DB8BC0F2E70BB557716BEF5B2A9BC9A80C6BC346DEABB11FDC5BC7D5A523DE80C56BC6A400E3E3CE1C73D6747AF3B8D7420BC6D1C093D05D80DBD6970C9BB2E3D843C6F71F83A87B894BC39B183BB8F36D93CE587C93BE689AF3BAD1819BC2D7E4ABC563F2AB90E580DBAA3EE503CF627C03BE54BC8BB082849BD8E36B93A244696BCBEAA163D63BE54BDB7498CBD6FE3C73C4B0C853CDFE768397267DA3B6B8F85BC665941BAEFC7FB394CF414BD93B9CABB12CCF4BB55AF82BC90C3E0BC12B22ABCE7230B3BB261EBBC40AB713CF0C9B3BA2C30943BC5DDC33C1EA5B0BC42C218BD5EAB63BA37548ABCE8E86F3CAAC3883CBA601ABBBABFEB3CC57C813DB8B4573C15F7883C7A50DBBC46238F3B0A7907BC8B6A26BB5A62783A4C9B3A3C4F7F3F3AFB21FE3A222A243D0E9BD5BC2E6D083C8168643C6868A2BCC42C383B0D12903C353B55BCEFAF92BCDB82D2BBA03CBABBD9D531BC435E8D3CE85455BC0F550A3D489544BC5C3C9BBA155E6E3C78F525BD7477F8BC28A4BDBC06A723BD7AEAFF3CDE3F473BDDBDA9BC16FB713D45F1D03C9489283ADB44BB3CC65BADBC5ADD3C3CE7EE7DB9EB4F083D258E5F3CB71410BCA13B663A3E54D5BC53D3EFB935748F3C2A957CBC93CC92BC25FB72BC5AD844364F8EC63B86B6DA3BAA990D3C7BDA2EBB393DACBC60FB793C2C77353B3BFF72BB876B5EBC003A36BD7B718BBDB96AB8BCFAF8473D8CE47DBCE0C6DB3CD076D03C8AE423BCAAD348BC22639CBCF460FB3ACCDECFBC7E3A35BAFB60B43B060450BCA5DA113C5FCC913CC58A51BC49472BBC62DD90BC7A1CBABC50FFD13B1891E6BB151A30BC5C7D3B3D869C1EBC1E6FF0BB27B79C3C0319573BC49929BD91FCED3C9BE7C7BB5ED87DBCF2C31ABC405D8ABBF48B77BD99C04CB9521B1DBC5526A1BB9656FABC5EE6B4BC939BFDBC378585BB38363EBD050E863C427A503D265D813DD5853FBC6750F7BC4DB7A1BB0A475BBBB248A7BBFE9EC5BB8BAF84BCAA000FBC8F7D9E3CAD2AE8BB9202923D2485FA3A2BF0213D037DCEBC3BF566BC7E5D063C07AC32BC7FDC53BBC04A213DA863B4BC70A254BBA68C953B6813043A5E47D6BB38FDB6BA7FCDC73BDFB9B8BBC927153C05ACCE3C2DDFFCBC7163A4BC8A723ABCA972C8BC761B90BBBAB9783CF81DA83CD511B23A44DBD4B98020343C99FC2D3BA832A0BCC875BFBC436BB33BE15F2F3CDB76873B43AE963BD1C4883CC00D013C138C883B127B63BC4A01AABB5F08863CCA995ABC86BB013C911A80BCABCBA3BBB7DFF9BC17998B3C950D173B7D743EBC46B90BBBF714C73C05249D3C7F5D4CBBE2ED8EBC317493BC356766BC84C08F3B5624F7B91C52A6382DA339BC0BFD083C7D7D923CC669B63A50CE41BC934928BD4B973EBB340D823CB513593C1B7A8CBD3CCB76BC9853D93B8D5929BCCE134ABC214A3B3C1172B93CC1CAF63CBB08723C5A6B853CD5DBBC3C71229A3C3DFC823CF8ECAFBB335B88BDBC2DF7BCE497943A80A6853C5829173D69A478BCCE2D683B4A64A3BB897A23BEDDE90DBE92F326BE68A190BC6435603DED1898BB096CB73C69CC8C3CC685753D806DD3BDF002003C4423DFBD38898E3CA89B1DBE52D038BDF106113D1398D8BB415B58BA99E2E1BB31F214BE6A9017BDB76299BD14AD0BBD07940FBD32B433BE90099BBC8E4CCDBD00A1C8BD8972F0BD295257BCAC5CAFBAE5046E3BBCA385BDACEE7ABD9CFAD03D71020FBEA40AD1BD9C31FFBD58B3EC3B430495BCE11957BDEAF9223D8393F9BC12DD94BC75B599BDC303513CFA3A22BC500A1E3CC596163DA704F9BB4945A7BC3DD30BBD4A0BAD3C1C2103BDAF2F1A3DB9B148BD156A333D9B379CBB68EF4C3C9F8F50BEAE12CDBB68D806BC9D65C6BD6107563A7D58D63C90B1373C16D54F3D67D9F1BD1CE36D3C3483C5BC965C09BD6B9106BE17B8FA3C82AA243D31B28B3C9C58AC3BB92D21BDDBEB9BBCD48F523CD7ACCB3C30478F3D85C0443BEB29383DD493A0BD5610B9BCC2ED4ABDC83AEF3CD3750CBE79AFAB3CD918C1BC2607B83C45F705BD607B013DDFBCF93C7938773CFB5C0D3D087295BD1503B8BD44AA8FBD5364553A84DE18BB5820BA395DDCA1BC5FE0923C5EDB1C3DD0EA853C5698A33CE615FA3CA5B34DBB7C16EB3CD2F320BC97238BBB98E9E43BCEC969BC7786D2BDA438E13D0A7EABBC60079F3C18DDF9BDD446893C471BDE3BFA839EBCACB1443C7BAD8B3C82B1803CC787533CD7B8BCBAC9E794BD70159FBC74506DBC570379BCC72B82BB072CF53BF723243DC47C813D5DF8B9BC1E33AC3D59B2A4BCEE1858BC199D33BDBA765FBC959A273E5E38BD3CF22314BD1BB527BDAEFA8EBD98DA5DBCB60B45BDFF629D3C3EE49CBDF7D437BD21C26D3DD237A33D03F500BD6B1E283DD56201BE502221BD249FA3BC1848453DFF541ABDCDE1F93C8249553D7E812F3D214B3AB9B411D8BD0B159D3CC07790BA1CD8803DE4348F3C93D3133CE5D34C3BEC43D6BDEEB0563D912F2DBD220BEC3A6E1EE2BB06768DBD35F51A3C56EF343C1B9E25BB078CD13C3E8DE33BB7B480BDE8990C3C3C836BBD1E6E0DBC26D5ABBC9BDB15BBAA66CF3D29F1D4BC8E7A3D3B7540CFBDCCC5EA3A5114493D1DF9673BF0E21F3C681C3CBEDA8EBEBD52F2FDBDD8DC36BE7190CC3BDEE5213D7CB7A8BD65BE35BD6264003D875D243DF83B373E898603BD25BD6CBC95819FBCD1442E3CF5EDE6BBB13B523D309DEC3D548A013D4D20983DA5479E3DA4A7203DA1356D3D541B30BDBB33D03CB78E90BC985935BC08DE4D3E27AB363D9D92F8BB28CD3B3B7BCCDD3B2BA1793D8359773DCDAFB43C5D6794BD316433BD77EF4E3DD70CA13BA4F9F5BDEC0AB1BB71F79BBD3D4594BD252E8C3DF3F4883CB9FCE73CF44F063D104B323DB3589EBDEA639C3B9B2B513DCB271FBC06FE6CBB7B31763C7D4592BC7CBDD63BDF4C9D3C13C4ABBD3A5BA53C4A6FC83C4A8D70BDAA1B50BDCCF8D1BC9C3480BC6C15CB3C2618DD3CF211D9BCF59F5D3A1BA312BD65592DBC12391D3D00F8233D3F38263D839D38BC466405BDAF02BFBCA59998BCDB566DBCD2F50FBC44FF3EBDC86EE0BC915F7DBC7893AB3B2543AABCBB23C1BD4E6674BD27DEBE3C9B627CBD99FF5CBCBCD1133D9A48E8BCEFFA73BC7D84F0BC1DE5F8BC7CE2A33DAB3817BD8E35DABC7467D9BC92FDFCBD8F9B773BBE8E853A631706BC0AD3053C86FD573D450E3ABD0CE2A93CC77946BC69B6E1BA3A01E839E6A7CF3BABEAAC39C64C7FBBDE70DC3C6EBE40BD801FFDBB7039483B9ACFF4BBC9E96CBBFE3F0F3D62000ABCF3B2B6BC0D9B2BBC1CA9593D830E5A3DB38DB4BC1888E4BC76EEC4BA2A152FBD0ADB05BC774560BD2738A9BBEC4B55BDA9D5093B865861BC3798D93DFCEC32BD2CC761BD8D8E1DBD7C0391BABD3018BDCBF47EBBB444EA3AC6D934BDD065B0BCDE47233B816F74BCC794D4BCC2F74CBDB0E215BDBCA0EDBC180D943C20EE2CBAE177533CB793A63C661F433C019359BC9F165EBD4BA3DCBC002C23BD30C99F3C379011BB067D67BD465DA0BC687F013DDAA5D3BBBC0BB43D13A8AA3CA84143BD560F02BCC8961F3D1051403B696FB03B8D9BB9BC4995AABD578C4B3DFD63923D0DF853BCC0EA7CBB6B28983C8CFE313D116A97BB7F779C3DB26E0B3CF0B044BC90EEEC3C133734BD5AB0D53BF9E29C3CA57306BC185E14BCAEA8D83B4F9205BC60D983BD9A73BF3D8E0AF33C8EA3633C737F953C9BCFB0BD0EC1FFBCE0D22BBDEBE80FBDA71B2BBC035F353D7C4D31BD4AF3C0BD061085BD50939BBC940CCF387CDA6939C702593CF285FFBC0B199DBC7D2BE73BDC6D033E44ABB6BDF84ACA3CD5A31C3DFE4E0C3CF1CBC33C758EEA3C6A5E73BCB2A674BCB4FE003D9EB1E1BCE8EB97BDE46FD93CEB6B04BA850F013C18FE84BDFC6235BDCE0CDD3AB472133DFF0AE7BC59B88F3D8F948A3D530C75BD88192EBD99E67ABC1CAE0DBD9B6B9EBCAEC5EEBB052181BDB204F4B90A6FC5BC4EF3893D93F1C63D78CA85BC3D4D77BC2037643CDF574BBE7B04BE3DC8AE23BD393D3EBC567F71BC97C7C43B987FAFBC7167EBBCFEB3213C61A5E7BC30EBEFBCFDBAAC3C8BF92D3D443BD9BB3441883D68F97FBC185758BCC8C39EBC544310BDC0D6CC3C4781DABC9DAFA4BCD0201C3DF50D153D54078ABDF29C513CF410DF3D0038063DC61CDBBC84F715BC28E5873D857BBFBCB7D4ADBC286E3EBD6B26E83D97F41CBBD22CB4BA1D24073DCCD5D5BBBDFE9B3C5FBC1ABD9E13A23C238AA73C323EF4BCA03AA4BADF6C94BD897637BDC3B2D1BC84F121BDBA5AA2BC9FF491398A0DB73DFDC5B33C07505C3CBA5499BD6CF1BEBC5AB066BC4E52AC3CA3B2D53CFA31013D72EDBD3DFBA392BBFBFDBE3D540DCB3C6E2DBDBC55DC8E3C2EC365BD9C13313D4AF56DBB14D3BE3CC1B8903D6346133CB678AE3C9746A0BBEB82463C182E98BB38F4CFBCD30764BC77A3A73CB17E493C9AD10FBD2BE61F3D87CAA43D544E62BA979637BC51C2D13BAE959E3CB2D31E3AF15898BCA5CD4ABD545260BC7D0C453DF9A05F3C08D50EBEB1BB0EBC2722C0BB07F01F3CBA42AABCA463163BF282C9BACDC953BD7D2098BDB3532BBCB4A1983CF42788BC90909EBB1BCCF7BB40129DBCC4D4B8BC89B20CBDF47635BC4425FE3C2D13B03B0FFF6E3B9B5042BC39923C3CB7B75C3C03DCC0BDB5C5C8BB3F2AB2BC9CDE8EBD5C120FBDDCF168BD9DF501BD298ED1BC0E0FA2BCE3D8303DA47B0DBD77E1E9BB03ED033BDF4A1D3BAF48FE3C6472413D4BBD27BDCD1F9BBD2BEE2C3B9C5E56BD54ECFE3B82FFF13DE0A8FD3B58DDBCBC06D6033D628C933DDE52063D0979DDBCF0EF92BD33A3EF3B335663BDD95ADDBBB58F923B832B473C763C58BCE57B803CCF318ABD33A9A93D8F5BF9BBA8D57AB9C2546CBD26A4C93CF3D0F7BC621AC33D540FE13B28DCC7BB3C3461BD8037603CA0E900BD67A0D1BCDC2D803CB6F1123D19D10EBCFF3D63BE8227CE3C483168BCC3FC12BD66B4A03CE290143C1230C73C793312BDE6A54FB8328518BDAB0F53BDE9AA98BAAF6C153D622A2FBDFA53D9BC9460513B37F790BCE5D90DBEE5D4D63CC2191FBC0C2726BC853900BCD6FDB0BB97DB92BC94F9EB3C044259BD6763EB3CFF3CF2BC60E5EC3CD4B551BCFB8CB9BD3D24713D182A8E3B92D92CBDA52E74BCF46DADBBBCEBF13C7B3263BCEB4A143E75C28BBC5A75DB3BC09F053EED5232BE4A03083DD62EC33D637E71BB8A3207BB61A37CBD3C4915BA781488BDCB43A9BC78098ABC2999EA3C6505833C0CCF55BC87F1B8BD146EADBC6F7B3CBD94BC853B999808BD10157FBBFF58353E152D983A1F9F8CBB9C38823C1320D6BD0F8E703DF4FDC7BA7178A83C5CB5DC3CE4E660BBE7A1C53CFA26983D68D502BDDAC5953C57493E3CBB20CCBC658843BDCEE9313DC899A4BC773854BC868AE93B7EE145BC3612C83B5205013C81F4623D040A3BBD56F4B63CC4E7D63C0C5CD5BD6BF049BD5ABA17BD3A75073D3F330DBCD719413D5C540B3DADC9233BF8194D3AEAFAACBD3A3F05BEDF70DA3D3256CEBC9D89FB3B6446543D5C4C7BBDA9D6E73965DCBA3C417FEB3ABBC34ABD236C55BD0BBB36BD77AEC83C437723BDAA445F3D1854AF3DAF03ABBDEB2069BCECE1333C6D6E13BC0E64173B5872403E8C03593D670FBEBCC068C3BC844DB7BC9E4F72BD85DE96BD2475BBBB65A8823D0914BABC47639DBC19308EBDE669CDBC3B153E3D34E49B3D5A2E503D152A2D3D200EA23C3101003CB1D19ABB87401CBC5F4C62BDF95E803B5FB495BDBFE5A33B85BEEA3BBF848F3B87DB34BCB0C5313DCBCC7ABD8F5DDF3C44297FBD6EEF88BCF986613D4F7F8CBC6F7983BEC4170E3D6F3E043D8634FF3A9972B73C824183BB66C11E3B935936BD55E1383C82F223BE310CC6BB1B655E3D76565F3DB4D698BD3DF29BBB210930BCEDF243BCCCB25CBB8B8D233E3672043C2E9000BC77B3B4BD39B14FBCB478FA3B27F2D1BD0476263C037902BE09D530BCD3046FBB7EA9B03B138BF4BCE32100BC1F179BBCF9AA6BBD8A9A143CC9BF94BC0DEDAA3DAD2EE8BBE3E254BC51B888BAD2C102BD3029BE3BA376623D7D84303C82BA423C988875BCAD59143CB28B81BCCA50A73DF5FD5A3CA6F98F3C783147BC0C86C4BB074BA4BD12CF96BC6913F0BCB6EC113DA69824BB1344B1BBE0ED96BD9A8C94BD1908A93B683D603ADFE7CCBB2C42CD3AEBBD48BD324D18BD3D340B3DEAC2A7BCA295FDBB4FB1043B7322D8BD01464B3CD050A7BBE3FAD6BC31CB063DC099533DF17D00BD588872BBDA41973CC7AA05BD950B83BC6B810FBEE8F680BCC41DFD3C65A44EBD8ED122BD4AE5A7BC796437BDCDC609BEB91F733C5E89823BD911A2BCBC2B5CBCD9619F3C17064CBD4A8FF83B454F9B3B3B79CF3AAC0C0ABD7430C7BD0EF0BA3C8DCCEDBCFD88503CEE8E083D7A2D93BBE218913B9BA3863BE4CECABC6000FFBB6ECA593DB9DB89BDC728713A7DEEE2BC474D06BDD1C532BD91DFF5BDA3A3CC3CB7E9A63DB4A0B93D277B2C3B7E63BC3B80B6BF3C62114D3C22777E3CF7A466BC9285113D44A669BCD67C0D3C2CA398B973F1C23852DF0D3C38E2DABC534D923BD61883BC05CB5A36B38174BD04F5A93CA1E897BC53609F3CADCEC0BC44FF143C2CACB33BDB4864B9E7943DBCAA13D6BCAF99AD3B0FF42FBCDE1280BB0CC7AA3A57370C3DDF1461B9DC84463D7A3E06BE437E84BE0E88303CAAD6ACBCD7F628BEF39AA13A5C105C3C84C292BCE1733FB8F46C80BD9B29B7BA444815BBDE72E63CECAAC13DD81C68BD0EA31B3CD02232BD60A8573DAD2B5F3BAE94B53CC1A025BC4A2B523C0B5BA83B986C0BBD3EEFC53C3317C3BC3F4DDD3B1B5822BD0924803BDDD9893B0C11CB3CD7DEACBC6405063CF786C2BDDE8812BD590F8E3BE6DBD1BCA4350D3D27F5AB3A4DF0AEBCA8619EBCCAC7103D2B748ABE88C80C3E15BD24BDBD8BE83BFC8023BD17942E3ED39F3BBDCA0BCCBD5395363CF63865BC1A033BBD0EE976BB02E6003C5E0DB3BBF91544BD215130BCEC7E88BB02B4DFBD9FED1D3C7974C1BCE719443D074990B941AA973C29A116BC562E403BC794C7BCE83DF4BD96B94BBD95E374BCA8587DBCE704623C23790ABB28951EBD22CCCB3BEB63A53B76B1EEBCFD822B3B715B593D8E2F643CEEED24BD051095BDACE6A8BC083B3ABC8C16E73BEAA36F3D9178ABBC7D129ABB753A1FBC5B6F68BCB43CAE3D321196BB60A082BAC78099BB063712BE74918B3CCA2CD8BCA691EA3B3869023C0B47323DBB1BCA3C7895DEBD9D068BBCBF6A99BC56D83A3D272DC73B2B751E3D8042DDBADE76933D6DD72EBD6A3BD3BCABC4DABD1D0689BB5AB48FBD90DF0EBBDF9C49BCEC6D1D3AC91AD33BF1F75FBC8FC50BBD106812BDBBE22C3C78E622BE2CEA463C0EE2AABD2BA3483B534C2DBE4E2129BD20C939BE580868BC1A991FBC0B2D75BC9300DABD8A256FBC24028E3DF2F1C8BDB65D03BE7452AABD05A4513D98BB1FBC836980BB45B483BC2979E6BC7C58D2BA30F88B3B04C28EBC4951B33CD3A1BA3A1197243D6B8732BBB2DBC43C1C9B1EBD0096D2BCBE2255BD35D782BDBFD53BBD6BE5DF3AAC4EDBBD988CCF3D53B345BD2E0A3C3CB9048C3B55ED89BD7BA1D1BA7318EDBCDA26A7BC5B47433C3254073DAC270EBE4E2FB03CE96BF53C32341DBD9AB338BD78237CBDDF9C44BB1A6055BDE99906BEE038453DC358A13C197103BDD2B88A3C43B86A3C939201BDDFADE5BD997D5BBDE9E011BE57A5033CB5B11FBE7D9CC1BC8BBAE8BC1F519C3C986ABCBBCD980A3D561D57BD9BFFDEBDABD79ABC209726BDDA870CBEABA08D3C478ADB3B7763383C8A6C04BD7BC0CCBC8F683ABE7E9715BB12D3FF3CDAC5673C1FC99CBC5E6686BC2459A9BD580580BD8630473CBE39993DB801C9BC0E86353BA617DA3C7C1A243A25DA6C3C19C7B5BDD1FF9FBBC942343DC05909BC1FC78E3DB77450BD670760BCA8BB3DBCD6B6463D20BC19BEB4E43FBA425D14BB5A966DBD63373E3C440103BA0E59153DD793263D6A1B89BC36D606BD8639633B54415BBCD93DD3BC12B72A3AA33D3B3EA8973DBDAE315E3C59CC39BDBEC891BD564100BEDB33CDBD3FAD13BC974B34BDBD980EBDF246AF3A1288043E585F823C760513BDC7A911BE0F8657BDBCD131BC2D07823DF367B7BD727F92BC6793BBBD2F61003D13E285BC10D9C8BD3880E53B4D1E853C6579C8BBF340D33CE2CBC13D29DDB7BBBA8C783C8A1C213CD2352EBB44DF4D3B70740E3CDAFA86BDBDA4F23B6D5F21BDEE2AC83B6DC2023BAD02E3BC4E2481BD1632053D82DA5EBC6B73053D60A05FBC3FED8A3C850356BDD684AFBC908DAFB8A7AFC6BD2F02563C4C1889BDE294003D567DC3BBB428D4BC17D727BD88EE89BDCDB9A1BD361850BC241420BD3E5D05BDB3A577BD1C7572BBA60FC8BC877F9DBC20E861BD7762883B75392B3D10F7F93B6591BBBC80C88C3D2E8A683DF050573DEF4D9F3D7DA123BC4AD9173D1E458D3D0BFCB0BD939331BBB8E184BA1ADA553C10F3993D2C040CBDBBF62B3BD8B897BC1CFC39BB3448AB3D802DE43CF3CCE83CECABD7BDA143ABBD763A053DC010143DAEFA73BC6BD18B3CFFA19EBC08B064BD676936BD4343A33C7F6DD1BC62FA96BB091ACFBD037835BD268081BB6A1D963D280E0D3C944B87BCFE82013D6B530FBD4E40E03C33A57FBB769D4FBD24C1263DBB82883C577C09BD112BC6BBA336B8BB664A24BD9C7B403C30F12F3DEC6151BC5881E53B0E63A7BC70D5AF3C299550BCBD3FBABC7EC100BCC03BE3BC2756513CF2E184BC0A4BBDBBD7E033BDF81FDB3AE94D533B6D0E913CAE7B07BCED9DDDBB9AE28D3C8D5A15BD118F803CE2E482BCD2F891BC8EE4F2BC4E66473D3BF7E9BB7C7D84BCFF7AC0BB054C91BC40AB143D429085BC6A37983C137FE83A3844403BCDFB743CFC369F3CBD99B2BB2401133C53D8503BE3EEB93CDFDE5A3C8020033C9774CC3CD503483B895FF6BB30D114BCBA2811BC03E442BC5E96B93B24D7B0BB41A6473BEE39023C1C121EBC3ED7ADBB25D48BBCBD178F3CF7E5B93C5DE22DBACA96AF3CDCBFEDBB0AF4CDBCB83AA8BB022205BC057D0A3C0ACD00BD4E1DD2BCFBA48CBC5C44923C00550CBC048871BCB7C297BA5ACEB13C6F0020BB63E628BC0DD143BC6C9D9DBC6386793C3E985FBA8D575ABA6D8170BCF669C2BB80EF5C3DF4A88ABCDD9E913C2B28F1BB56BDA03CF3D8E43BD632003CC6905C3CF6E9FD3C28F390BCA8A934BC7810C1BB364CB6B97BEE5B3CF34CCEBB6066B8BC32E7803C1365A5BC438D95BB1C1A09BCCA27E5BB1FBBB4BCEB2745BD421711BC0FE64EBC7BD7853B445C28BCDD114F3A12A524BCFA78063DCBC960BBE460903A542DB13C99A07A3C0D3DBB3C0A84333DF176793CD4E3B13C57D1C6BC7294FDBCFE3A2DBC08B391BBB9909EBC8FF2283B038D7B3C589390BCC80E2BBDFA7E133CB6860ABD561E0FBC00E5363C88E400BC691B08BC3968FEBA044A6BBCF76B01BBDA54D13B6378E8BCAEA889BCD71B03BD07322CBC0B7CDDBB458808BB843E34BC0E5FDE3B29DF42BC0BE4C73C3F46143DB7AE9F3C4BCBB4BCDBFB16BDCCC189BBE3963B3CEC7722BCF6092A3A6929243CA623A5BBCE6E1FBD07620ABDF94E42BD5F0EF2BCBD8CCF3CAD24533DF9143C3B76E3973B9BBF07BD9DFE223C769C523CA243643C5257D1BC4764E73BF6D99039CD7026BB08734DBC0C7BDCBB638D3EBCF00B933C968B983CD67E7FBBECF6903BB79F4EBC8EB60B3CB492C3BBBE26EFBC7594343CC000E9BCA032AC3C53CB4A3BCF8DEDBB1FDBAA3CCF139D3B00EF0E3C4DBC123D86B4F2BCBBA05BBBB254AE3B405AF93B96A482BB03EB003DEB871ABB9153E23C2770A0BBB34AA4BAD5E58FBB7D6C043C6B3DC1B9B867893B0B0CE8BB1F4E883C223D303C203BB7BCD137793CC17B5FBCA55D45BC526C70BBE6E63ABAB2862D3C3043DE3ACF961FBC7B1982BC90BCBA3CF6C6393C7203013DA947DABAF9463DBC6F397DBB92EE5EBCD60727BC9D9961BB8DE234BB6902C8BACA94383B7BEF5E3C765E03BC1D4AFB3B290FA53C5188D43B588650BCFC1B7EBD3A9519BD4485D03C3B8D7EBC56F1E2BC53BA0EBCC080EE3C183C753C5BBB4F3C43F396B9BCA92BBD441E12BC9FF700BE771A94BD9A582BBDF65D0A3D32A5363DD0091BBCF43499BC0F6375BBA28ECABD67D2CBBC5B12ACBCE3A6513C53324ABC85C1A0BBB1A75ABD169F04BC8FD4333BBEBF053B348A63BCB51FC4BD227DACBB2DB88ABD2EBB95BB2C6586BDE5ACCB3B9C9B1EBCF818203E8CE57C3C5F2725BB3DC50F3D8089493C78B4AF3AE05A573D42D4953C372E313DBAF0073CAEFFDE3DE082FD3BA0EB2CBE634974BAE55444BD27FB9EBC1103FFBC3621C23CC70DE1BD4F3B39BCCB8E1EBD06CBD6BCB24EA4BC16C31D3B59A187BC4811393BA344ECBC24F8CF3D4A1EB1BC069E98BC676263BD8686E0BA58E9FA3DF3E462BBB0001C3AB7378FBD74C673BCB247CB3B6CA9FBBD32BA833C63CEBBB91CA3D33DCFFB673B0DBC8BBDB3D616BB31AD4A3D8172D73C9783ADBD489D173CF726FFBC1290CD3B573E903C9EA8123C40E8E23C94E794BDA3DE3E3DBC7253BC80BCAB3CC5B787BDA2009F3CF16BE3BCD4DA9C3D6D9A2DBC9020E13AA44505BDA7F5283EA1A170BD8694A2BDA24C9B3C28053D3C11828ABCB845903C48F61BBB1CD090BC1CB7683B04E288BD97F339BD255F8FBD8E2846BD70EB043D5642383CEB5236BB9EF73F3C1C80C2BC65D413BCA4C51DBB80FC39BDEAA75EBD3B95463CB1BC4CBDF37A9ABD117E2B3D8B1EE5BD2AA5AC3D0EC439BDC284933D0C0EADBC9919AABD6CC0AFBCB7F4B73CCA3DBEBDA040A23C49403E3DD034AC3A376B443C9C8757BD0B9BBEBC002E0BBEDCA855BD0FF833BC0B8157BBC1AE683B40F4F53CA7EF143EBAA586BD8460CEBD1BED62B91FBD0B3D0AF6403C52027CBAB3BF86BDDE93433C876E183A1768F73C22A5D0BBD20187BC87A4BFBD2B0E2A3C324B343C904176BD428A033EFCBBD53BCA659E3CF0712FBE6EF064BD253C2CBD36B1BABDE58B0BBC0C53E63DB0748FBCBB8A373CD06F05BD388D7E3CE41A02BD2E22D33BA48E30BD2756D43C8D7FB03CF000BFB99F1C97BCCDADA73C195B543CEE34F9BC3827BC3AC91FBBBDB7CD1DBD4CA908BC8BE9243D57E10F3B97091F3D819808BD25BC17BCD2BBC93C7CD6C8BDE0CB553CAC7046BDA7D8063DCA72C33C94202DBD2C364CBD99FF133D606849BB8B5CABBD9914B8BDE38D213DB0E03CBE47ED8DBDF6C0F1BB91A42D3DE50CC2BCF3EC8EBCFA7F80BC5D60823C081DC23C86A0993B51DCA43C08E086BD7BF923BEFB2BA63C84F86CBD22CA2DBEE571EDBDDDB5C4BD9DB0D9BDD314373D5E7B093DD50D153C44DD4C3C369AA7BD40FBFC3BE61F8C3CD31C4ABDAD08F7BDB82F30BECF6990BAE18366BC7411353DB349D8BD29F2D53C69F21B3E9138C63CBF779B3DE5A07FBD5916A2BC810B0B3C7CF0DFBB4F332DB8CEE5FBBB8D5908BD2F079C3B6DCF83BEA1FDC63B434D25BBEC61EBBA8F9049BCE19ED83C54BA15BED775ED3AC0DD42BDDEA9D43CDE0DD2BD5CFF3DBECCD61DBC7DCC03BCD59A8E3DC7F3043BB7D371BD23C60A3C13FEBC3CF9FBAD3C7BE2553BCDFED1BC4DBDAF390FC728BDD6C422BDAEC69F3CC6DBBEBC5925B73B490823BC5F34B6BB8AD25D3DAE3B553B850956BDD40E6FB71E9107BED77FC7BCA30F27BDDE4390BDEA3315BE95E6DCBAFD48B73B43DDEF3871D904BE10E202BEE1F93A3D50049DBD09744DBE52BBFABC565228BDEABBA8BC6A38513DE09399BBFD2701BDEC52583B656D37BD183042BC1E7A2C3CACE9093C2604903C5DE1CAB9E1E132BD4979E13ACDFCADBDCB17ADBDE81ADDBA70EC8DBD37DB8F3C40C0BD3CFD24093E34F4D5BC82A57C3B6B59FE3C1498863C94FBE8BA95BE91BDEBDCA6BC9C0BE2BC19A6943CF3260EBE1027CC3CF5E9513D2D2314BE97240C3CACBE193D3B12BB3BDBAAF5BCB8E44DBEFE1FCE3BE5A412BC0896AEBD3A132B3C7ED1B43C980942BCDB581BBEDB054EBC8E3FA8BDB4F92DBD289D14BE2968E0BCD59F5ABDE99D833A8D4CA93DE74914BD285D31BD345C94BB910E46BC428DF0B93668DCBD31DE46BC8A79273C90646DBC6231F3BCC5B2D3BC77B561BDBFEB6CBCC2BB003D03C249BC148B62BC381D66BDA91CB0BB269C85BC5C34233C05377D3CB52582BCE94C1D3C65D68ABD9FBE00BC195C45BCE49DAE3C259915BBDD611CBD67D8073DE042173D68F2A1BD791DCD3C1655DEBC9E88E33CA3DB31BD9DE8DF3BF65EF2BBC26C1ABC234EB1BCEB9E8DBC27D4F93C798A1EBDD7F11DBC36C340BDF1113D3C207119BD2FD1BDBCEE60B2BC74298C3DC4A5043D3ADA7D3D988508BE543336BEBEC718BD2D503ABDA4D3BB3CD69D043DFA1D3EBDB4AFCE3B96E5A13DC30D973C1656CE3C03CFC7BCCB83E03DABA5C9BB77C39A3D5646AA3CA8B1043C4E8D41BD3B7B293D2FFE4EBB872724BE0B76D3BBC8DAC6BCA29C813DF2FE4E3DD177B7BC1B0502BC6658043DD202C9BDC9B7A53C5B29193C3AB30FBD2542E7BD54800E3B12FA39BD6B95BCBA4900DD3BE0276CBD5405263A0CA0F23C0CACAEBBFB07403D60CE8DBC4E0B11BC42928DBDB7ABB1BDBBD8B93C5595C7BDE5BF76BC588426BE7855633AC126893CA37ED0BC9354653C237B9BBD8B1783BD003DB13C64A4D33CE72E4C3D96361BBB6C34DE3D8BA090BD687F8BBC0BC2973C13CFC73B2FF4693CFFBD3E3B7DA9883CFCEA513D5A6B673DE2D5683D18FD9A3DB9CA813D5D1BCCBCD6C465BC39C605BDC2AAD1BC9D0123BCF30F703B049ADE3DD0D6E83C29C2183C5665C5BBF41413BD3B05F63CBB92013BB4261C3B53FAC0BC42E1C0BDB003F4BBA27222BDC2BEE3BB496598BB5AECD1BDE7BB77BDBD1303BD40FD83B96AD2C6BC7E9892BBBA3C52BBD28B9FBA5DB0773C1A6EF53BDD48EFBB3FBDA5BCA8FEC13C2225ABBD6680BA3B9AF0933D620818BDEA0E233C624F5F3C91B66D3E3A63853D78A01C3D62858C3C4A01633DE00707BD83CB2E3CD91C2F3C12540FBE0B9C2EBC21824ABD71D6323D8F2BC1BBCB6426BC3C5EABBDF1C010BC3614D83B458B773DEFEEC23C76D00BBDF9B86ABB6AD1BCBD9DBA953CB30CB8BCA9F8213DCF4F8B3D472612BCE4A9BEBB8945833D829C55BCD683F53CD099973C68A190BC2A12B5BDAF10E13B08A2BD3CA81E653D2FFDFDBCE318CBBC9D7BBCBCE985BDBCF400B2BD15847A3C00F31A3D722BD0BDD5F83DBC780C823CEB0E79BD9F777C3D5979853B2A3EA0BC9BCC033D50A8123D9EB07ABC3B359FBC9A4C8B3DB1451ABE366EB03BDCD8A93D0E794EBD8120F339D518B3BC754FBDBBC11DD03ADBBC95BD6A7DD13C8C6E43BC09992C3D17CDA73D585913BD0F37633B5ABC953CE96D883A9521DDBDB19F5DBDE52E8BBDA083563C0E59113CA68408BE07DC0D3C89CB2EBD62AB3BBC499F2DBDDC572F3D5A10EE3C687BD43C870A693CCD63943D8732C1BDAA6C8EBC215C9B3CFC068C3DE3D5C4BD4FD9E4BD93B4AE3BE62F153D737242BD1A6323BC5EF134BD78AC06BC630DBBBCAD2FA4BD887F553DA73A60BE4F8DE4BDB25B8ABCECF4A13C54B2A2BA337944BC2D9156BC1EDF113DB7611DBC5D53063BDF51B93D655C7D3CD36A323CBCB9D6BC285C433D6E1E20BD9F88593B887F29BD52282F3DFD9DC03CD16422BDB690A83C5191323D8AACE4BCF2B08F3C6C1A533D97F5A7BA4AFF4BBC4B2B92BD9695FB3CE1A5ECBD6E1D1BBD5FEC22BCF61F273CC016F23CB83F153CFB67683DC98DAFBD2AAD1EBB817ECBBD8FA92CBD832D2A3D00C364BD8E3A72BCA601213C33F99C3A053CBEBCB9FDCCBDBEEA0ABD522CBFBC84034EBDC5E8893CD6F3B7BB5AD4943C5A7A003C37C06A3C65BD77BD9F7603BD4A3221BDF8FD6CBD66C1603D603CB83D39A8293CC1A30B3DC90130BC867D84BB5C0C66BDC5DD4E3CD393A7BCB94E18BD4E5FE93CCC121CBD78AC7A3D2607ADBCD187B03B62BF1DBDC695173D6636FDBD4D6C263B150A0ABD5EE5F7BC40EC40BC366D4F3BE94DAE3C716603BBD2F6DE3C44F1263EDD1E01BD8FCDF83C402CBFBD9678B2BC74F896BDFAFC1DBD1EE7453DFAAE2D3DD31AD83B5A1EF3BBF05361BD26D42ABE2743253D421DA5BB99E4C9BD15BF1BBCCEAB69BDA75387BDCBAD01BD7687E1BC68163A3C57EC8E3C2CBEC1BD155309BEC1E60B3C9D358EBDF84930BE4CFFAEBDC8D617BE9145AFBDF13CFB3C1568EFB91AAFB93CBA901ABE2B461ABE9108573C77A6ACBC0A2424BDCA1DF2BD1FF8F3BD6D0A143DAA4F71BD8808203DB5A7B1BD6758163DA490193EF86D25BD1B844C3D4344B7BC3C078CBCD2A7AFBD8B088ABDF679CB3CD38F703D3207843DCA1AF73A010321BE4EE494BC3EAA90BC0D5B44BD7164843DE47E64BD58E395BDD86620BC5086293DF5AA393CB31E9D3C688F923CE3EE1DBC4F4F9CBCC398863C881F033E92D7563CA74C133B32AB14BA22FBF03B6B2DEE3C64278F3C2D58E7BE0CA92A3B039712BDD305E6BC19A03FBED2AE9DBE04ADAEBD5870D6BC74A6DD3BBCAE2FBC4B45A53CE4163FBCE209B4BC882198BED0561D3DA7BDB4BC9CA8BA3C5C01933909911EBD1BC9BA3DCD6D5DBB4263C63B31D68C3AD9CF4FBC6008D7BC65D247BB4F7FF43C9D699EBCBB14B73BB73CF0BCC577D03BE773733CD93D833B8671633C1FA43EBDF76A22BCCCDFBB3C26C184BC58F18D3CFA1D593AF55B233D2A6F03BC7F45033BE06F593CB325743C13D69DBC072404BBC003DCBC37E392BD81407D3C93056A3B2284F8BD2D63EF3C8B4F92BB40B79CBC903A30BC08DC03BB226E1BBC101531BE13EB4FBCB9A0DA3B538A8DBB012344BD1CF5C33C67AD4A3C8B8EB23BD41042BB3CF83E3C78002F3D5C83F4BC575A36BD52806A3B2FC557BB6B60A83AEF3B433C5E97CC3C3EFB5BBB351561BB2700EE3AF8F1563B11FE0C3B720627BCF36FAD3CF32ECABBEE58ABBC3434F43A2FC446BC075DFDBCA728F93B3AF8763CDE3F22BD10A6BA3C99606DBC59DB3BBCE8A2C3BB484B7CBEC26D363DB17B973DCCB1BBBB28CF293E802DE13A5BA6B53D3510493C20E2DF3CC6F495BC2EB13DBCC169A2BA3B6DAD3DAFBF1C3D35E4E03C5FD3BFBA253196BC5747CDBA07888EBC46CB90BB90D6093DE009D8BCA42835BE2CC089BC8D1C25BD881DB1BDAB57CE3C92C2613B0371B63C83B80B3D4ED5E8BD340D873C49A4AE3C165239BB71ACC33B6846AE3B6A8EF63CDB7D02BD5227023B470508BCAD3EB63B2C1094BD7A4630BC1DB56D3C0541F13EF2B6C63B0FBD02BF2A2A0EBF9E88273CCAA86F3CB7B900BCDA88EBBC9479E13BF9678DBB6011493CDEBEBDBA4C10E93B0498D0BB136E933BAEC577BCD8E85DBCBD32FFBC5667F33C880115BCCD72903C957BD43BE38CE23C200FF6BDC8A41A3CC358F53BF7CD8EBEAF6C403C686A58BE2804F6BC4F994FBCEEC146BCB577053B889ADA3EA14F1A3E6B8EB8BC114F17BEB7E4763C7CD981BC02EE3FBD55DD9ABCBA1F3F3B3B77A93B64CDEDBC69D068BC1363663C0FAC453B6E46883CBF3942BB6B81053C6AB14F3CE01115BBA2DAE13CCE1883BD2C34D5BC73AA4BBD9A0A123AECF7B7BDA5139DBB6272343E9907613D2AB9C23BA73B3E3C6308E13CB81FB63BC5066ABCAE23DB3C97D5C53C58DF813B59CE98BC630EB13B6AAF723A8752FF3CBCDAE5BB5DF611BCE457903EAE1D5ABC1DCBF83AB9C0453B150C833CB988683C87CA4ABC578B01BBD1669F3CCB4C333C9186F53B856D32BBFE66F73B6C5E4DBB235CAC3C8FFF25BC3FCF76BC974BC1BC0711F4BC9EA7A9BD9312823C842E71BE1FA78EBD620157BE86520D3B579309BC4B8C9CBBA34191BB5AEF8ABDF741C2BDCCC5E2BD292D283BAC73F1BC914AA43C77C5443C9A50DB3C33A4ADBBD8308DBBFCC427BDDA298ABC38EC5FBCEA7ABABD90BC1A3B5FAF623CE7F7B4BD525883BD0FB8CC3B9AC439BB9FC654BB5A9B193DD8E883BC68C292BC075205BD8E13E1BCC08AA93C8723F8BB216CE1BCF2B389BB834EB5BCC684203BD53638BD0F7A183B87659E3D6CC8E6BC7CC63DBEA83BCD3C93C0683DF814DB3C013AFBBDB85B84BC8EBE5ABC1366F13CE4EF13BD6BAF37BC915ED9BB49A01C3D45B0123CA746ACBC821DC93B2B0390398D9DCABCFED4A3BCCAF8C13AAFE3C03D1888F2BA72C094BDF53167BD2DA32BBD138E11BDC63B623BF3341ABC5659853BEA2C85BDD24C5B3B42CEB63D38D93DBCE6D720BD3E24A7BDD319A4BC2EB747BC6ECB513C9AA2183EF06B033D419E16BD66F9D73CD1EA173D9E4C23BBDD26D3BCF76DB6BD080A3C3D5E3465BD247A42BB684D923D3163B9BB7E3C07BE600A08BC9C7D3B3DEB6CA63C8BB912BDC082053B1D9D4EBD36C3923D211319BDEBF50D3E117788BB4230CDBCDA6F4E3B1D24183DA69AA5BB99EB99BC6074AD3C68CEF2B8997A09BD5CD82F3E772C4B3D2CB43BBCF7A048BD0F2F6CBB7A0482BC2CB41C3D812E7D3CD3A1383B2C798FBD03251ABD6FD2C33B331C2B3DAED948BD3B8CA0BC64FB73BD20CB6B3CB7755ABDD441833D7C44A1BCE6BCE8BCF5EEFF3B6E66B2BCD02274BD0C76C03BFE719ABCC581B83BAE0556BCA5222D3D7DA396BC0ECAA1BD374AA6BD82B03CBCC118463CF1161B3C3467583D07A1A63DE00BA3BB661F883C23AC5A3D3CE7293D6C32063E39A6FBBDAA58663C12AD263DA119B83BF77A4F3B1EBAA63C4558BB3B2FE92CBE7F3661BB623BA93A50D144BD1BDE8F3D3C62633B768100BEC18D59BDD13632BCA4138ABD2BEF6ABDFE6ABEBC6B29193E5A54A3BB28480EBC822DB9BBCB3BB03BA052FDBB856E50BC22B72F3C0467103D0FF59139CF0FEA3CBFAABFBB9778153C6C1FE2B98594FB3CF3C68ABCF80410BCED7E913558C738BD545609BD13AAA7BB252EEBBB13B7A23BA8113EBB5F7C0DBEADC417BD6642633B12E827BDBEEE0DBD2B69473CB1D244BCB0A0783C8200EBBC0BF6ACBDC0A918BBF65E873CB7A9B83CE87808BEF690E7BC3A18583DC1DEAC3CC3B5E9BB94BBD83DFC879D3CE6B6CE3CDFDF8F39DF2754398074D5BC595B183D55BBAEBDF6F493BCD53F71BD4618B9BB4B9620BD6F7412BD267CE83C0E32FA3B60CAA33B041DC03CB3D9E83D1438FC3C1495BFBA3A9C083A3095DCBC330347BDF8F3DBBDD4021CBB045E123D025648BCEE47283DF628F3BCE9E1C8BD4637EE3A11C7E53D66E0343CDE18BFBD47B44E3CD1EC05BDF3E944BC201AE2BB16F106BD88278F3C3EE5C4BD2F15AEBB33F0AE3C75FF863CA0D4FDBD1A9DB33B5599ECBDE0FAE5BC642CD0BD2176593B23677ABBFD6587BBEDF1EA3B952E1F3A12D6333C569F873EEEE6E03ACB821C3C5E4D623B617E18BC7164CFBC2254EEBC037E95BDA952BFBCEA9F933B0E7E6C3DD01656BE301AB1BEF748983D5ED6DF3CD7CBD73A7C7E2A3C27B44FBC8AA018BC6BE0B0BC00F573BEF061633C4E2516BC1DC06CBA45533B3D3161C3BC8D2550BDDA9F4EBD32F1CA3CB104493B32B21FBBA83EA43C8218BCBC07414CBCD2CF7B3BACCF78BCBEE8A33C1B67323B64F9493CAB245E3DF15117BC77B87C3B2369073D51147FBC1C8D21BC74CDA8BC0D9595BBD7BF5E3CE111E6BBF8D9ECBB43B1083C4403273D1AD16DBD9AF3773C580587BC5125B3BDE29181BA4CEA1DBD42E805BE13CEA6BCADD202BCF89DB0BC9F849D3B0F2DC1BA2E4D913C774583BDDEB5FD3BDE7EFFBC858C4C3CE58FDA3C28938DBC4E2996BC7475B9BC53E24A3CE558383CDCC634BD67AA553CA9FAD03D67650BBB053A633CF01F3FBD2A4289BBA3E9D93B7070613DA910423C55B135BA22B1BBBA059E8DBC1A8F233B6C7A683CEF5190BC8A6C44BCD62C2BBC6F3D22BBCBF28EBDEF7E53BB5062E03CF9476F3CFFED0ABBDD26A8BB66A749BD41C432BC6D7B2FBEA3B9B6BC9EB32FBE155BCCBC310E1D3D276F1B3CE781C33C868FEA3A78F2973CAB2F933AABFB74BCF6C23FBBF8E3F2BBD6DFC0BCD787B1BC805ED1BB3513AF3B11DA633C6BBAF0BB225B84BC4FE61DBDED6DABBC834300BEEBE900BB42327DBCF08AC6BDD005CDBC33AE26BDB2668F3C461F85BCC966BEBD39F9BE3C0283203C864B3CBCB5D2ADBBA3D4FBBB8A5609BD69D2A93C0A5B11BB2767F2BBBB5B54BD76C5E7BB1CA277BDB985073C26BC8A3EDC3D89BABDD88BBEE1F560BED850DABA82AE443BEAF06BBBE3B8673B745DF7BB36D66ABC6E44B8BA6B42ABBB629BBC3C6104133C81D85A3BB02B973B789F013D668E083D64EB74BB3EDE57BC930B03BCB01DF1BBD2E6F8BAC84685BD419EBE3CD19D4BBA773C04BE4A7B223B070415BE508C1F3D085608BC862C93BBA981213B7DDF673E601B543D368B07BC5B7C433D467D81BCE17CC63C940BD8BC3F2A2A3CBB4AD93C802905BC7A2689BC443A033BD0E0DBBCAB776CBCEC696FBB9FD3143A11A0E53D69A7583DF2CF533C6F47C2BC6B8FFFBC2264FFBB1244413C18191FBC4E4D103CEC911FBD3F29BE3D8FDB5DBDCABCDABBDCD7DA3C9BC5B33B542E05BD7A2CAD3BDF3939BCAF2F4A3CB60D0FBB6166643B2EBFBA3BD79F2EBB6653FA3B2BA57E3CD70D87B9D224223EF037F03BB01FD7BB6331BBBC99A1E2BC50999FBBEE137A3BA9ACB43B40DCFBBC7665353CD4C7523C9B149A3BD3E6843C46AE38BBADDE003E6D7E9B3C9E4EACBB30FCFDBBFF9F50BC2BCFF6BDB357B73C578503BD679244BCC73EC6BE252CC53C20AA363D3221CC3BD419ABBC6FFA0BBD05FF3238C26457BE94027CBDC7744ABD3639423B40A2643D42477F3C89599FBCA124383D530E48BD75E60BBD16B06EBB11C9323C3AAC12BC918C403DD166F0BA0919103CB51B5B3B9F5D37BBC3F9F73AD34CFEBC8CE40ABBF23CE4BC8D12983B91B61E3CDA6487BD5D51A7BBFEB4C03D13AD02BDFA91F1BC06EC0ABD403C46BC0FE5B13A91AEF8BD0E4BE3BD69F0193D99D0C1BD4770E6BD584886BD954386BC436D05BC49EA6A3D6EF7A03D71E5C4B9344FBF3CE13318BDACF1103A42756B3D7DC7FE3B4943B9BD259B163CA32436BE87D8D43C4844C1BB8103F4BBCE1072BDAB581DBC027084BC907B613C28360D3E4263ACBC5E8290BCA7DFEC3C2443023D484F87BC4770C7BDAE29873C2D1701BEF5AF793B6975B7BC9926653DF495AB3C780A52BD9E0F643D0CA17D3D5C8B593B8E18A7BC4E1D4ABE0671753CF9CAEFBC3AB077BD31D30C3BC23052BAD4374F3CFA82FBBDA0EB133D4C43B9BD1E7196BCD98432BE9442D0BC32F686BC0B3B013DD09603BD3A8BA8BDFB8C1BBD7A5D853CD8CC6CBD148EDCBC26F0E3BD29597CBD33F6AEBB3F8B903BB39D97BC0C4957BC10127C3E4B2E9CBC5618BD3CF3D412BE88E94EBBFCA445BDA68D52BDFF50E23CA32AE63B696E2BBC124CB0BC5D039D3CB32AB9BC3FDE04BC4FB60EBD9B913B3DC6DF9EBC57C8B3BD186C39BD3B23FCBC1048C9BC77C78BBC56BB6CBD129A96BDD457FDBDEE16A33CED0378BCA7A179BDFA2C1E3C8C7265BCD3853D3D4BF396BDB827F7BB9737763CA2390B3DD8E911BC44DFDCBC34D295BC3BDB2DBEEE63D43DC345C63CA07D86BDFFF2C2BBDAA492BD18F0E3BD2964A1BC3C245ABDE911DEBD2CF7EBBB6E838C3CB7B7B83CC8A390BCD26A413D7B84443C6714403A32BB2E3D070AE63D7B8AB43CCBD1213DC6CB523C50AFBE3A66BDF2BD458F033B963B4C3B6E94DF3B99FB423CD35F09BDB0C7173B5433323C72118CBC52BEDB3BD749CC3A655EC1BC6DF410BE209048BC80AE4BBDCCBAC1BA11B94DBB671D65BD12402ABD8BA5CC3C5CFCB5BCA8F8973C8736013D40A8733B5F8179BAEE1CA8BC18F58B3B9A108ABD0B8078BD9D05F2BDC255343D67B6393CFD7B49BDEC4D3EBC4564A7BCD324F9BCA3C5763BCAB0F0BB418497BC303DA2BC726A983D1AF08C3A7212B0BB4756953DF8BA41BCF563963CBA0BD33BC6C8D33CFB7280BDBB5BDCBC8C11333D4C58883C0947083E342E11BDEAA540BE1B1927BDBB564B3B97822E3B4FF2C2BC9993383DC622063D8D9D863CB69787BCD6B38EBDF676A4BD8FCF003D906765BCC98C86BDE14EAEBD6172E8BD2CF65FBB80D3C9BD06DA313D3E2A64BDA30AA1BD6C01873D63D9F13A164B37BDF0ED91BB5EF390BD67DAD63CC1B17C3B25818ABDDD4CF0BA8A3C17BC59C51C3C0A4C09BBD497273B1C7BB53D785C14BC51B1E4BCB9B1A1BDCA21DABD0B38403AD6A1F8BC27F790BBA5D68B3C77ED81BB3F8F08BC1A521BBCB978DBBCDDBAEBBC7B054CBBD293A8BDC69584BB507F2F3D765076BDA1D295BD328780BB7C8A043D7577503CAE67443D7761BC3CB513B2BBAC80F2BCBFBB06BDA751053C3399F33C2A792DBD7BBADC3A7AFB97BCEE02ACBDC20F1B3C9A700DBBA8728A3D3A0805BC751760BE65649ABA918C813D32DC3E3D985CF1BD3C7772BBBF42403BC37FD83BF2292FBDC8E486BC5522003D54FB1CBC6AE4ED3C84881EBDA2AD01BC8419603B32A660BC5F5854BA9BA931BC7999AF3D8297A9BC712593BDEBFDCBBDC61E29BDD9E723BD0D8C813BFEADB4B924625BBC2D4289BDDF05F23B488EAC3DE8D23BBCF5CD1CBD95008CBD4DB96C3C9F9DA0BCB2F7B73B9F4F083E0044443D807E0FBD7A6101BD9538C43C954992B8CB404EBC003F8FBDF78F043D17228BBD13F9E4BCDC945D3D45060B3A806CD2BDD089B0BC9BDF103D457A3A3DD66F7CBADB810CBC2EF444BD86684B3D7516FFBC2363F03D1974A7BCBBDDA2BC8A91D93C49FF953CB4212FBC7374E0BC0E446ABBD7CC1ABB465ABD3B19C52A3E961F713D0E556CBC914853BDA57F9ABAE93FB7BC00F41E3D26E15C3C14F25DBC4CDB76BD07D86D3D587B693CEC0C4D3C679A5BBD9056F9BB50897CBD7439CBBC87E4F8BB15BBB13DBE5D40BCDF7FA7BC4E96503CA5372A3B1F4D4CBDC7EE9B3BDDBBF0BC7C7E64BB8EAE27BD8BFBDB3C0541343DF03DC0BD7BE8D6BDB4C181BC229C483B02244E3CD143C63C01024A3D10A9B5BC32B4D83C4990713D10A019BC2BF20E3E3714C3BDF94FD23C00093A3DA7A62A3B55AC973C0FC0373DB05979BB18A20ABE17E188BAD32B04BC932027BDF92D913D92E692BB1FE708BE2F2350BD01931ABCF6E122BDBACA53BD8740EF3CC66A303E774F8D3C7E8F83BCA6C24C3B03A1F63CAE7AE9BC0FD31DBC638910BBEE5D053D7E0ECB3C4C11BFBCB2DD33BC1315B43C197B57BC6556423D01159EBB9B719BB9E81B62BBF794F3BC7C08C6BCB9922FBCAD35D2BC4F67FCBBBE4D593AFC4781BDFB03A03DA52A533C81748DBC4C27F4BBE5D6213D500D23BD7C1F003D09BA70BC30AEDBBDD6CF4E3C113E663D61AF89BDEDE806BEAD7083BCF780813D82B19CBC29957C380E85993DF4CB4D3D76AF90BB9AF584BC208CA33B645821BD2185E33C171AB6BDC439FB3BDC5A8EBD1B7BE6BB0FDDE4BCC689C2BC0F6F0E3C1D5B05BD517EBB3C70040E3D14A2063EE7E93A3B22BBC8BB748D84BB905F383C5D2F55BDC7DFB3BDECD9DDBC5142873DE3588E3C0AA83C3D62AC9EBC820299BD067F9F3BB047D53D20CE1C3DA9477EBD9ACD9FBAE6B16A3C8E7C1EBC8073EF3B4BC801BDAFE595BB003994BD526D35BB6E9310BC8C3F67BB1A43D3BDC3CA52BCA095D5BD7F78DEBC8AD622BD58F2563CA1B4533CACD90D3C9C5B9B3A91AA013D2A8D013DB461543C580D223B88B36F3B1B64663C34CBFD3B7E8B69BC5786E5B9A193E0BCD6B2813C6AFA7FBBE67F2A3B786621BDEF63833BFA6ADE3CCEF9123C459B4FBBFABBE1BB8EB7353B9B5742BC7C99ADBBC149BE3C8FF8EC3CCD7FA4BB8AA20D3CFDCC23BD723288BC9BF4E3BBAB9768BCBD66BC3B5DFD983CF3FEBCBB0455AEBB09B5A93B3BB7483C331DF93B30ED8BBCD4AE35BB8455A13B49FAC93AE151C33CD6E8103DC7E7763BB038F43AA649E43B0139733B82477CB98285CA38B94EF2BB3C57123DCC12C43B4DC068BA8AF2533C2B07223BCB0E753C366B58BC846F99BBC5E6883CEFB9663BA8D2E6BCF3BB34BDF3BF99BB3A0D553B361A143B76FDBD3C43A8C5BCEF8E38BCDEB0613D7F5B5F3C515232BCF1F6963C5F557E3C054D42BC4A75D2BC4DA69C3CA5C404BA833FB03B5303E5B912FDAC3BF3998BBC612C723C9A1B1D3B0D3DD9BB28C40ABCB3CE2B3B16EC4ABC075BBC3CD9B245BCD26DC63A3F7015BC8E82FDBA3630353B1C7777BB7F96A7BB8244E33A0256E63B1175DEBB133A54BB3E56C93B0BF8ABBCB9BED6BBFDAA91BC7820193BAF36B2BCE7CA10BDD4B527BCBF6691BC02A70EBCC00F203CA85FF23C61D256BC35A29B3CEF5C9EBB7A289DBC64E45D3BD815F5BDC330623CF3A71B3D66E896BAAD99473C77B1183BEDFF0DBC1D38673C8790A0BBEF9833BD16BC70BCD184A0BBA512ABBC9F79BFBB77223DBDCE0CAA3AFED7FE3BD47D23BCEFFEE1BC4AF5B53BCE959E3BD8D91F3CECA504BD5EA1CCBB51C3BF3C4CFE633CF60A71BCB63EEABBC8FC6E393A8C05BD9952A9BC9D55463CBA558C3BD7B060BC7F67FF3C2FD9E73B9D45423B02C534BC8692353B7F5FE6BC582D1EBB4A63383C6D15583B822A1EBC14943A3C2295EF3CD87E6A3C80ABF13B3ACD88BC28C2F03A5B0699BA0CE60E3C1A88743C17F786BCCF611C3C2345E5BB01D24FBCBCA24E3C458C40BB8F323CBBEEC8D9BC82CB71BC28F2533BE26FF2BB6E67AABA0459E13C781689BC08BD07BCFE840BB9968C453C4E8AAA3CB030AFBC98AC3FBB223580BBCD9793BA374F0BBBD26E84BA062B293C9900163CD8D900BB74B7D6BCC17928BD3D021CBC089CDB3C1999DA3CC66C533C832A893A2649A1BBB3FA3E3D5C1D90BCDAF617BCA17A55BCE3A818BD5C493DBB115ECBBC2812B5B9D3061FBD5EDC14BC5DB1823B49B72BBA434B473C1301C1BCEDCBE4BC3FB4BCB9EBCBA0BBA250B5BBF10432BC89F056BCD80FBD3B1ACFDDBBDA1A91BC028801BC9BB35E3CFDF49BBB6E61053C319E2FBD065F4B3DAB15F8B8D03F113C5D7E603C7172773C0E9ECBBCF4B7EC3BE0D2AA3BAA7E3CBBD247DE3A1D2D19BBA041693C22F7253C1A20A0BC0A85E93BFB361FBC7D981D3B7ECC29BC9F1ABCBCF06078BB3682AD3BB9181DBC42F63EBC1C900F3C6E4D1ABC5A64AA3C87CD923A653986BC2A3163BB4D2E8DBA079ACD3B06EA9D3B7680EAB97AFA77BD2A4A7BBCB2C1CABB90543E3CFF112EBCE0777CBD6AB6E93BBDCC973C00D56FBC14B75E3B83F675BC2EAD1A3C9C1A8E3C2D952CBDBE64D23B6A21203C67EC04BC7F73F3BC6E13E5BA4A92383D9207CEBBD3FE55BC1C8CE0BA2A4283BB4BCC1C3C63118BBBFDC89BBC4F0B553B3A5D38BCE1F727BC7E45733A86B9B63B46A30A3C32051FBC90CA42BAFB39753C040406BCDDB2923C6F2A07BC1B82093BBAF684BCF110753BC275C1BB3228C7BBE2FD9F3C5EACE9BCB4D3A73BDF6A533AFCF991BCDC44123B5DA185BCA7A022BDD0A78BBCF73736BBB39626BC8B9E9E3B3A8E843B4FA709BCDDC810BC7350DD3CC49392BB1F0A1A3C67D7DCBB8E4D953B602912BB821981BCE7B67F3B2F528BBADFB79BBB7AAD1A3DDE86F33CB4B763B939125E3C464F57BC81B7CC3C53314FBB5DB57C3CB6BD853BDAD804BCFEE4013CE30589BCC25884BCC9CD503C874B38BBD53E69BC44FC983BE014E3BB48089CBCE725413B897789B90C85DFBB1A5FFEBC1CBD573B6518A3BBF043B6BB31BAFABBE0146ABC6F628BBC7C13ABBCDCDBE83C2EFB1FBCC94C453CD4122E3C93A1C2BA919C853871BFFD3A3C391639FA520ABC927007BB4DE7A43C9F84F6BB8EA73B3B1A6EC03CE1ACA3BC9F2E07BCECDAC4BCED688FBB0DD886BABD0C2ABC1F5CBBBBE9C4333DCDE7F1BC72D381BCD866663BB1A2913C984C3DBCB5E1EE3B27735ABBF535A3BC310898BCBFC45FBC3A8932BC99A98F3CC65631BBC259393A311B4CBC16BFADBB8DAC99BC0FF5A63A04FB3C3CD4732E3A853D3FBCFA98BEBA0A8A32BC5AD953BB665CE0BA81D28ABBC2DB0BBCD45E423B49E9E9BBF3E0B7BBA7E7EB3CC9535A3C83E1273DDCB69B3A1349C1BB67D2343C315693BC64C9D0BB154B0FBD68E63C3CEE77F83D4C21DCBBFD470BBB8B62D8BBE88C4DBC995281BBDFE84BB92F1E873CE20D4F3C209CF3BA0957F9BB28631A3C90E24BBC9A585DBB3729543B4A346ABC3F4E0E3C02925B3B698981BA459FBD3BBD7CE8BBDE389C3B542D0E3B157F47BC0558CF3BCD6A233CD766D03BB0938C3CF972583CA4DD64BCC51E4EBB8046A03BD21AC03ACA6704BA2C048F3CAD5A503D74BA2BBB5EDA263CA198CFBC252B973BBE822A3C5122A7BC7BBE14BDF9ACF13B53413CBC603736BC12779ABCE4E04EBBE1421A3A99ABC8BB7C294DBAE1AAA63BAE3392BBA37F203D098B0D3CD6B9173BD487223C5686A7BCF5FD0E3B254B58BB623CBA3A2A1089BCC92A5F3A521792BC606F583C84D517BC459FCABBA0E609BDABB2E53C65AEA8BB3BBEE03A83BE55BC963A45BC3FEA2C3BE1BBF8BC6F01FCBCBA1E16BDE11F7C3BF35464BC0FD0A8BB2A3C5F3A63A00F3C9D4C3ABDBE6F1ABEDAC28BBD6C39C13C26C50D3D826C063D4068A23CBFBB83BD482A273C2846FABCF9F690BC273A213C270A353DD5B6E6BB802EACBB7A19863D19EDFB3DC1461DBBE65BC43BFEB73ABCD0F0B3BC961EA7BB9D1B4E3CC028E8BC602AF73A786385BD0007A0BCE8C10A3EB7BEDB3ACA7F9FBBFF6E983BCB5DA3BCF3DFBABC12A1E3BCF611A3BDEF639BBC5A0518BD9EA97EBD7ED4FABAA8EB293D7420E1BA5C280C3C8112D2BC4965CEBC3EA535BD879A7F3D6354A83B816E6EBC68A366BCAEF9123D4B2511BC7C01E4BD45D898BCD0893BBCF1340EBC0BC36ABBF82127BD034DA7BC5791803A3ADE00BD860E2C3D1B0B693C585EAFBC5ED8923C0F25B23B75C576BCC914FA3C612535BD6B2654BCC36EBD3D75199E3C7F445DBC78260EBEC556CC3C111B113EC93F083D027EA43D571D67BD1C27C9BCCB8414BDA49277BDEC7ED73CDBE1A93CEB73AFBCC2E949BD1D7BF7B9072E2DBC982A1CBD18087FBC6BD2A7BA1B55C63CF7EADEBCB6FE13BDC5A0BABC83CA1D3DFD8025BD61613EBCBEC2C03B2A85C6BC3BD381BCB91680BC78D904BB73311BBBB77F2CBDAF0D7C3D43E7BD3CCBDC94BCF8DC63BD0B07453CEB9E573C142A253DA6A8643C811B13BCD27998BB517675BDCB30CE3CDAD0C6BC27AF06BCE3AB85BB8B3C223CC1324C3CD1DF35BEA112AE3C5F7878BC99F0ACBC34E7713C7C88143D8072263B29D95CBC45C316BDFFD59CBB1DD306BB9DB1423D7595E3BCD7BB793D1C08F2BB7B21CA3CD9920BBC3A9F84BCB9971EBD17B0F5BC26BB053C39FAEBBD1A8F843D350CA7BB71D9C6BD4A8B8D3E71219F3CFC6182BD7B2E4DBC8B5F143B912DA4BDC8E6FF3B3FBA783AD4BA673C9E437C3C4D32EA3C6A6017BCEC8BCABA146A6D3B7E7787BC33B8B7BC2FC33A3CC3CAF4392166D8BB8D9E95BDFFF6C3BB74090DBCEB96E13C7B53E7BDC0AAE73C6A09C53A0188473DD763343B744B52BBBA56FF3C9D42E0BC8F8FCFBDDB60FC3C48D9FA3C52B6D03A59F67ABBB754A63CED92A8BC7DF4833D5AE1F23CD1154D3C7D694A3BDAA04B3C5DD8A03DD3D6A2BD67EC613C924767BDC2A8E6BDC09DB4BDD8CDA7BCD2B9CABA2B6C513C8E50133DB17ACF3C2ADB2C3C08A17EBC249E933DA66EB5BD4E70B63DE48A3F3DF56417BBD4E80A3CDA749D3D0734563C1E15073C307AE5BC6458593D6E0126BDD6D9D1BB65F1F73C9B0DBE3C4E1ACD3D1ABC7F3DBC3DDBBDBCAA5F3C0141C4BB372F573C539895BDB0F226BEB328293B06BD8FBC0BE4F6BB1C6A93BCE9FE43BCDA84753C11FDB7BC7BB80A3D5EA22BBD6BD9ACBDF1D7C9BDA9A59D3D19894DBC8F94F4BDAA6C0C3C9D9036BC6AE7913CE846673DA509D53B5FBED3BC69CC02BD67029E3B6701A5BD0B93ADBBDA3813BC94B8BF3C43F94DBC67A7C63C602FB53DABA4E0BB45BEB0BC39385CBDA28C5DBA4E6CAABC5BC55C3ABF07C93C85E63D3E81F5B43D3D53763979DE923C2540A43C8683B2BC680B20BB6228A5BD789100BC64FF9ABCF702D1BD82B6F13D864457BD5FCC5BBE92ABA43CD67ACBBC232115BD96F2C7BC3085DC3C453D1B3D8E6C99BC994702BEFE57943CA9D830BD83C9C2BC7DD654BDC0B46B3D6EB30F3D7281CABCDC0A88BC6964DF3C241B0CBD57DFCABC69043B3C1100AF3B49D812BD4EA7DEBCC4F8AF3C4D05F3BAF9B925BD426EC4BBF2236BBE35C6B5BD283BD13CCC9409BD8B9F71BCAF13AABD9B53F0BBE4FB7BBCD719923C1F3504BBBEE0BBBCE8DD3BBB2E2E09BD5C38C83D8774D9BC8884F3BDD2BC0C3CDACB8CBD29CE0FBEF728283D3BABBA3BBE551BBC44681CBD8D373CBD644A843D5951FCBDECB4D9BD7199D23CA26BAD3D9556233D9D9A413C044CACBC9363DCBBC179F6BC1B70723BC8C4813C20987D3D859252BD1B260EBCD21FB53CEE3A2C393F94883DC587723B8D0C603B86B2B73CD8E9903C4003BBBCC676803A64428B3D332D873C3BCDFEBC230927BD7E02CCBC77715A3C929E17BE129157BC8099613D523A79BCDE94F4BC4BEB3F3CFBB79BBD7E2F6EBD97790ABDD7B6D43C7575093DF368A2BD90D502BD8512E6BC09CC7E3DE7D5763C36C0C4BB47CC24BC79E45DBDF7EEDBBC4BFB813B02697B3D98CFD03CBFBBA03BE2C36EBD7C21DA3C0113CC3C5454FBBA307106BD3E3050BBB72DACBC5C540FBD14899CBCD8DEAA3DB29683BDA56920BBE00935BB19B2C43A73BA1CBD8392B9BA97E4913C1E50C53B748F153C0DFD69BA81DF10BD802A943DC7DEE13BD294B9BC3F4126BDB83C073D6F1DA7BDAEB58DBBF280133E2301C13C644381BE172BB03D524FF1BCC9F45C3D5B8B81BCF45D79BDAA9B65BCBEC97BBC2B68ABBCFDFB13BD4D00103D60ED763C87D882BD96A380BB8B439D3D7ABA103EF0D8D8395484BABCF22249BD79DC023BFD74EABD4D57013EA5A885BC8569263C9A97A03CD70A23BCDD1049BD351E31B9C7C1DCBC3C9031BCBA70AEBB800821BDD14DA6BD3DA45EBD1D5F433C8DE09E3DF99A673DB50258BDFDB830BD19552F3D2DE2103D72ABACBCBBC21ABD6489E4BCA790113D324487BDCE28B6BC4EEB243DEC2F6ABA4ECF0ABD3F00893C7CAF99BD29D88BBBB2719ABB0119D2BA21AF2D3E7110DFBC274096BE632F29BCE670A73B2AC5A83D1F4C223D5D2946BD932499BC64D24EBC1B05FE3B550F59BB8D7BB73C1D4458BD427C0CBA64DE9D3C60780E3DB7A80F3C5FEA36BDBEC813BC1B580EBB30DFD2BCB4CB7DBDFC1463BDBB4EE2BC15B792BC583B173CED9946BC91DD4A3D9AEEC9BC490E28BC9C655DBCF8E1ECBD8FDD4E3C2E7421BCD3AAD23D970E4CBDD282AD3D4EBE303CBEC0CDBCFA064F3D70C0BFBE85EA383DFE3715BDDE48DEBC37FB4D3C54AFF3BC92706CBBAC82B2BC6477EEBB005235BCDE1750BC34DB923C095216BC85F496BBDA5E4B3B440915BCE661D73BCB722DBBA0B54EBCC80E61BA074A2ABA5B92513C1B28B5BB0DEA2EBC880AD8BB35EBA9BB6527193B0EE72DBCAA1AE0BABAAE86BBB686B4BB67FE3ABA6C8EED3C29FCF8BAB766C73B106F10BB9DA16E3BBE83873C437F453BCD963EBBF9862FBCD6A751BB2CC640BBB98FF43B3BC12CBC8CD9FDBB0587BF3BCE5FBEBABFF31BBC5E13B1BB4FB612BBD52239BC6FA48E3C37C3A33BCBBC403A90A5B0BB3E81313C95CC133CCFDE25BBAD44963CC31D5E3B7CBF883BA12605BCBE7C65BB0D2FEABB87BE9D3B3F71403B860038BCD30A8ABA148653BC765D34BA2E87AABA3072BDBB5959F6BA0A7D3ABB720B213CB93795BB13731EBCB175C1BB4023123C7581B6BAD283363CF81876BB9B1B4C3B60A887BB5B9E25B92598FA3B7BFB3D3B2848123CFB1C1EBADE838E3B546784BBD54929BCE254B33C16BA58BC7AF14F3C00FF26BCEAB0CF3A26F7003CDB87C4BBF77B6DBBA2B24C3B1912F53A68D750BC26360F3CC4FCA3BB8CC3313C14005BBB15E68EBB4FFA91BB3B97613BD809723BD28518BCA391163C7FD7E5BB1C02DBBBD0CD0D3CD79146BC2398AE3BA7BF16BCB0A1E7BBE140E5BCAE31D33CF03020BB5E27BB3A502156B946DD83BB877804BC5FEEB73CDFF2D8BAE8C81B3C05AA233DD2DCC53BEF5964BB3A32363B3B3A433BD9370ABCE22B523C8EBEA0BB37E11A3CC10C36BB786989BC1F0C9DBBC4E6DE3C86BF8FBC877675BC1502DDBC4E4707BC633514BBE1288ABB14A6CBBC0793A5BCAEC1573CD873D33B777F03BCC1D8943B34708DBA6DFF803AD2D2B53BE263763C8BEE6CBCA33CD7B7ACF61DBB50572FBC89CAC9BBEF298ABCF94805BC4AB535BB17AAC8BB0F6EC33AFB10DBBB2EE75CBC7944733B01C8DB39B0C7313A943C2B3C9C0AC0BB3279BC3AADFEBABCFB7F743C720E013E1D690F3C89591EBCB70A61BBB4841EBCF27550BC850129BC73FD73BBB400BA3B63BD713BE4714BBBEC70C73CEC719BBCC81532B923E1873CE8C88DBCD1EC063C4882853AB06074BBD9C8033A83A3363C2FA01ABB0CF432BB8A3428BCF1DEADBB5E5F543C3B749F3BDEB2AC3CD599093CFB3845BAA300D339514483B973AC14BB6B5DBE3B9524CEBBD16E663B745EA4BC1AE029BC1B5D18BCFED6053CA8A7053B64A271BC0C20423B1A63D43BEE5745BC436542BC386C02BB74AFEABA7F1D24BB1F480ABBE4B2F03A66FFB4BBC09FBF3B939DED3CF24F7B3C67F864BBA8B0E7BBDE78D1BCF73AC63B3DBB26BACABE6B3CCE5E1BBCBB84E4BB527688BBBD690EBB522D25BB0CEEA13B452720BD33127E3C5B67823CB4D1093C6B3B0CBDBAB2C9BB23ED5E3C67C5EDBC8F5D29BA69AFE9BBF01281BC076DF0BC87E36E3B5D7011BBF2268C3B446EDDBCD5EB1FBD06A0193D717A80BDCB4206BDF6051A3D912C9BBC2267AF3C37CE9BBB7BC9A9BCE05695BC9F7DF8BCFBFB3BBEA644B4BC0C49043C46FB3B3DC1CD9DBD7AEB8ABA6919313C04B82539C59A39BC7B5913BDB862BC3A82FC78BDDDF2A2BC1BD2DE3B4F412C3C9845C3BDAAFA45BCC4111BBE98F377BC59B090BA066C143C82DFD23C3973C43C4FA1E2BC832F08BC95C3663DD796AABC95CF983D298BF4BB100DACBB6BB39E3C3DFD4ABC8C00B53C4327013C1D46B0BBCFC51B3C4BA124BC986453BB6882A13AE2A3BA3DA8DC05BD36718EBC8A5DFD3C1663D1BC9C8B74BDE618E4BC37348EBD06994CBCAA17093C2F62CFBBD7A4A2BC34E04CBD61E14D3BEF7D52BC9C2BA0BD805F7C3A1E0EBCBDBBBBFABDE4CEC7BB24C3B4B9C7F1A83D45C7AFBB7D7FEBBD1DC086BCB7D95CBDCB9F73BCF5CE823C99E27F3DB19B5FBCDB9B13BD68F9723CB57E59BDB0E450BA10A69CBDB67E90BDCCA2173DCD3E26BDECCD9CBC2A23F13C806F3FBD9BE81BBE9B86023C8CD6A83BA939523DEF9AB23C16C14D3C964896BDE6AFD03CFEAB8BBBDA2275BC0BDFE03B848BD53B4BE6123E2478283A9111DD3CDE240F3D7D7013BC093C863CC001F9BA4B00153DC67E793C4E63F83C21A00F3CE615DFBBE00C0ABC36C8963C56CFFEBCFCAB11BD161E3DBC4EABA63D8402A63DD2F06A3CB52D36BD7B61583C2340A73BD46C223D98F882BCB0A0343CEE67A03C42D215BD8CD07B3C518213BBA3EF8CBC1BAF83BDD4CC08BCE50CB0BC864865BBAD9028BDDF1A373DBB0A5C3BFBED15BC56DE6E3C0CB589BC8998213DF4C461BC14670B3D456087BD8A5FF5BBFFF1073DA33CB33C3EB12BBCDC94E4BC51B307BBCA1893BB3D22E1BDB6464EBEB0EE47BC27F207BD384112BEA3F3D0BCC8A6DFBD0B1E6BBD5B17133B37DC9FBCF439C5BB9D348FBC2DD7DC3B5D85CB3DD04A4A3D1C7C7EBA6AAE693D3640DEBC867D59BA4DD7F5BAF1F2953CE6239E3C15F571BCB341013B0479933C483FB5BB7AF8113D65496DBD301B0C3D8D5AC2BB1C8F283C72D92EBC71588B3A8EC2A7BEC121293CCD2EAC3CE2E9CCBC1B7A0F3D605B503DF71153BB668E123D5312D13C63B317BD5F971B3E2C3F923CFC9C0CBC817E39BDBF79463ECA4A11BD8DFA81BD76D51BBD510926BD23990E3DF8913EBC59C138BCFD5E0A3CFB3B9FBDF1D3173D640251BDAA22063D5B445CBC0F8F3F3CBE20573D0BFBC7BBF5A0213C1EA2303C9946BD39FF1E84BBE830F1BC7AA351BD471BA83C75E0BABAFA5B193D4D832CBCAD1D73BD9BBAE7BC4E9F273CABD881BD3C854DBBC2388A3D0C745FBDE76199BB28AC5BBDCC4059BDC4F83CBECF5C5DBC904E88BD21D17A3BFE8916BD4D94EC3B7711A53BBB0E1C3CA7E1A93B92725ABB4970EDBB369619BE204574BCA1B086BCCDF322BC9003193CDECF4ABC61AC003E4068A13D7D9BE73C4F4C823B4B419C3CCA30C83B24796D3D7977AF3C2546FDBCE8076BBD981B30BDD8D85F3C1B801ABB69794D3DEFC43BBD993C783CAC20AEBB1EF2D5BA970380BC5F5100BDD0F6AA3C66B6D7BD6591683DF814013B44D8CE3CE596D4BB79E532BC8E89CCBD2DB1703DCCDC413C8E462E3B9F24B5BBA77EA0BD4F0714BE1EC08B3CCF28AD3BD5F49B3B1BA9B7BD5621AABDF9A5C2BC37B65D3D1C3C893CF280953C9B2C07BDA3C36ABC5C0A053D490DC9BC5261B23B8B16EBBCDD01033C71FA40BEEAF637BD6BE0FF3DB23BA4BC93C5C8BCE1AF193D85C73C3BEA0A38BD44ACA8BBF333C9BDE29B083D147EC33BAC67033DFA4EA53CC7D15BBEDAC8A3B968BD80BD243353BC3AF6763DE7C8C03B3837D4BC4E09973B412555BB111D99BB9A633C3A95B558BC401FAEBC1DCA4E3DCCF6A53B1D70173C86C467BDF4791F3CC1BD953DE696AD3CCCF75A3CA1DF0F3D798F96BBBF221CBC5B386CBCB7364EBB544FE93C59879DBD1B528FBD3A603A3CED7BE33C3155C2BD840ED8BDD7116FBD364C1DBEBA4082BBC477BC3BF48839BC6C7DEBB9F6CB13BCD96A30BEEBAE093DD0D586BD6FD3033CC35488BB6AED32BD828F123CD28DCEB9F6CB3DBCC9DAE7BCD84FDEBD5867A83DD66B853C988F183D991F1A3D7606C0BA1370C33B47C277BD8E72E5BCFF3F43BB9D4D0D3BFD3D083DE7CEF7BDB0D44EBDC495DB3C01C6DA3BBD278FBCD9E37B3C3619A53BED72C1BD2E206EBBA7DCE8BC9991A3BC84F8BCBB8309E9BB3ED26ABD0A6F8BBA78728EBD4F1C463C219DDB3CFA732A3DE21F98BD189367BDF28D543DE95F593AFE824F3D937C23BE83B2263D9C7541BC0A7417BD79DA9D3C34C9043D0BC822BC4205BFBBA17122BCD345493D2444623CFCDB0BBD3C66C0BD8E98FEBB1A304B3C0612983B92F9863C8142503BC005B63C370A7ABD158B9CBB1998BDBC0AF40ABDDDCE22BDC89DD93B09189BBAB9C8BEBD850938BB8078643AC8BD07B7B1A2D9BB5BBDECBD490D02BEDF3803BD49AF96BDB259C23C7120073D71AD4B3C8A19B3BC185896BD326498BB02F759BB7D770D3B52E7833D3F26E5BBB90092BD18063BBE2302903C53C49CBDFF3B3ABCEBD6A6BCAB9DD63C6CD6013E1F524BBDB0CF2CBECD0BF73A90D4073D3C3E5DBD220F88B8CEEFDBBCB2CAAC3CDDEF86BD109144BE21F00DBB56D03A3A6C4AA0BDCD5529BD30DDD3BC2AC833BEC3E61B3E1579BDBBC4242B3B4DF06E3C12B04ABD81C038BC998A693CD7A8C5BBAA3CE03C2A3E1CBE22E7B7380A0F0E3D13E766BD3BA8ACBDE921473C1371C6BD2C86463D78B0A8BC1BB30C3B0D822B3C7A6C80BCC4748E3C4CB2FABCBB9DC93BCB7BE9BC7559C23D3BC3F9BB8A311F3D5820953BB1A1123C3BFE263D59E80B3D723AC73C134F51BCB493A53CF3FA3BBD8C90E1BCAAF8A33D4D629A3D92CA9BBCCEB09A3C3C27BF3C295ABABC1DFEADBDAB7B8F3B4AF7DDBC29A00CBCCA0138BCC32F163DA729C3BB8EE453BCBBFC8D3DBA94D63D69E8333CAA1BE4397C7087BC1762013C06ACC43B39518FBC88C75EBC28E63C3CC04E953BDDB37F3C4F5449BEDE5AD1BC1FA681BB97831A3B1101B43C92D5823C03A02ABD5557DABD3876EBBC150759BC2B6E92BC731607BDCEA8F33D238CB0BCD66ED5BB5A1754BDDC702FBC8DAD9BBCA5CA373D64D233BCA49B97BB1D85A1BC0601D63D0BE6B9BB060B0FBECA098CBC053B37BD87B4BBBDC67361BB784C9CBC0FE114BD610E19BD0CA50D3D195ABE3C707A42BB266123BD43F1ED3C1690AE3CC22DB2BCFE401C3DAFA4A4BC7642F8BCB7FD223DA1D522BCBA5968BBE846C73D1EE8D0BC6AF67BBD80DFB1BC0E93A93BAC558B3C14A628BD0A5B32BD7E722DBD20FF95BDA1BE473C3EDA24BD3317123CD5E7193D4B3CC33C37E6B8BC3684D53C6DB71139F9EE6ABDA89993BDB3BF20BDA43603BDF7354A3D18658CBC355E0DBD95960BBB1ACDF9B9FBFCA73B195391BCFBF7A9BBD38E5D3CBD931CBCB36335BED9CA213C0E2D26BBE43C69BDA2AA8CBC846552BC156714BCA10883BCF92B4FBC1E580E3DCBBA363C125B303D8ECCCCBCB5D7CDBCB305E03C96B1553C9097E73B005627BE59866A3C9E83C1BCDF4DBEBCD0EC553C136201BB879F81BC27ADF33BDB8222BC62CAF23A62F6873B5C57953DC5E3183C76DB00BE77C6EC3D95FFB43C80B76CBC03FDCCBC26F8BFBBE43A263D7D39D1BCC09F0D3E743734BD0CAC833C9FE1E83DCF2717BE6C05F7BB6D24BE3D644DE83C3536F3BCCCACDDBD297112BC5D4E84BD30EC4ABCF827BA3CD913043D72A6AEBDC454A93C4F9C5CBDE72026BC19AD46BC7561D0BAA7FE7ABDB6B18D3C4FA8D63D925242BCDA9076BB1CBA96BB1B02B2BD5800993C366C8ABB8ACF603CAC7CF83C62334B3C783EC9BC070A0B3D6A12CFBD193CFC3B7E569C3CEA35FB3CCD2DFFBCA0982F3CFD6DA2BA6CF1403D7DCD6BBB4C2F88BB87DFC4BCFB16093C0554123D9D076BBC59E232393275CE3C3B9E22BEF68024BE7B5F1ABDB0AAAABBFDA506BDA886103D6E0C8C3D2F9EA03CCE8D7BB85ECEDBBD12644EBD32DEAA3D38DEBBBDB7CA513C6DD7BCBB17C561BD67F89F3A3BB0A73C5E1A543C8335C0BD901CF1BC1E5894BDF8E92F3C1D56A1BD17478DBB5219883D95BF21BE75FDE9BC13CC4FBB8863FD3B35E57CBA1227B63D2A2F31BD30CCF1BB5D0685BA895313BDF3DC88BDAF4F7FBD64046FBCF13ABF3C8ACD9A3C4C99E6BD907E42BD8DB01FBBBD1248BD90B2D13C10769D3DF1AAE4BC9EA85EBB82F96A3DAB7F223AEB3AC53CDC7CEF3CD9998B3B109B0DBE959CBCBBA7C711BC71491EBCAAB6E53C9031CC3BF1AFE0BCDB48A13AF4ABA33C436CAD3CBCE0013E365C19BE052AA9BC87075EBD923FE63C5367C0BC525DB73C5E21DABC4DC4A63D454C0CBCA51A0D3CD218BBBC474786BB8B32E1BCDCBC3D3C1EB5E7BCA318533C6242F33C746FA3BBF3A6253B19577EBDE5C8A33B502402BDB74421BDA2F7A4BDCDB82C3C4543D2BDDC4C87BDD0451ABE91FAAA38622BDC3C0B86F53AB26AD3BDBE8EA2BD868C80BCE52278BD27832CBE77181CBD5375B2BB89AEBEBC8C9B8E3C6F6CF4BC4928BCBDA99A7E3BACB339BDE07949BC8397FBBC437D2DBD3212DBBC2E30A539688B11BCF09D6FBCD31F1B3C6E170FBE51105EBB827F16BE5716BA3C07FBB43CA8586B3DFC3ED5BC6FDCF6BB4D76B83B1B1A11BD2F9B07BB50FD44BD6EEFDD3A0F8ECD3CB480B93CD061EABDA051523BCE96303DE04ECFBD0D6156BD3EF389BDB72B24BD673D1ABD198BF0BDEC54A63CFC2F443C572F10BD854F38BC647B29BBF11A28BEE707F9BD4D19E8BDF7A7A6BCC274E13915AEDCBD2E2FC9BD7CA26FBD99F3FB3B0D4A6C3D2E881ABC1BBA07BDA69A90BCC0B6113BCD2B323D383D8DBD59D8093C573D273B5DF13BBC3BA422BDD27A29BD4EA09ABE4CC55D3CAD219ABC5083B93CF3B8803C3245ECBC03FEC3BA325269BC53B7503B3B179B3DB420A83D7785BE3C22652F3BB89ED3BC03FCCEBC46F4CCBDA155E6BB7D003CBC335CB63D49BA6A3C274559BD21BFEA3BF9AE3ABC8D5CF53C69F2F2BC459C643BF1E45ABC943482BCB6DD75BD879CA23C00039D3C818785BB83B692BB66D5873CBFA8593C3EDEBABD2FB548BDF603B33C41042D3EF4B988BD32371BBC7CA093BD8506EABDF188DCBC5895533D4B93763C69FE9DBC60B5E2BCD81D92BCFA75313EC9C42ABD6974AE3CBECC7ABDB59B8B3DFD75A73CA5ABF23CC5CADCBDB2EB09BDAF1F98BD440B443B2E05F63C1239A3BD204803BC1F3B193C1C7F943D960DE33C53D0DABDF90D073B8905AF3CC0A113BDA903DDBC19926EBCC13817BD9D3B5EBD3F54DB3C1418C0BCA5C0C43C05C943BC64A216BD107B18BCB2E43E3BC33FABBC95E5973C3331D1BC6794C53BD8C727BD7815CE3DB013A5BCED18DABC8798B339F62048BD561766BDC90146BC139661BCEFA551BDBD6B8ABDA42D60BD317836BD539721BC8CB47B3D2C54C33C3DE6D53C2B5184BCCB6DE4BD412332BDB18B8EBC6663513CCF1904BCA5571ABC9AAEB43D68378F3D6D993CBD0E08693D8320ACBD7829443D3A3A1B3D45C9EC3C13E3ABBD36CA77BB14CE3ABD91290BBD7E0D79BCA73631BC7B4F0FBCAAE97DBB10252C3DB56015BBA494273DA8F2F6BB0549BBBD6C71853DDDF27EBC742CB03C45680D3D57CBACBD86E114BD98E269BDDA70FCBC520B9E3C590C933C9349B73DAFFF843B6505E2BB6290B13DECCF2EBC42BC9EBB26F2D7BCC30F37BE99DFA9BA486B9F3CCA0D03BC3BBBD83CADB8343C1C2D5ABD4AAFA8BD435A83BE122074BC2AE4A23B96C14CBA6E184E3C462815BD8B03253D5B554FBDA7B975BB1A295F3DDDBC5FBB6CCDD2BDD792533C661272BDB1050E3C4E233A3D79BE693C491444BEBB8B1A3DB67381BD70F1A03C3BF931BE0DF79EBDD974883D577C08BD3134C13C5CCC3DBCA833993CEFF7AB3C8CEF8A3CDE27C73C2E214E3D85F1C13DB7A6D5BD834F3F3C298595BDA7749A3D0F8E1A3A6115C43B815F903D68F194BDE4A3683D4B3F28BE0A13C2BCDAB82CBD7DA543BD7C144F3C90FE823BA3B8723DC38C083C9E69523C8FE08C3B6F228C3C1BDC4ABDC661C63C9FAB0B3D377D6C39C4EA03BE9AD4B63B018CCABD066C9E3B8332A8BC365D063DD6FD1B3D1621FD3C1009C5BD817F2F3DDCBD8CBDF252C2BD1FE43C3D7DC933BC3A5CB1BDF42B13BD7462823D223DD13CC8F28BBC46B5773D81749C3DCE8D0B3D79FF1CBC61E49CBD55504B3C2A25B5BD28472A3DBC8204BD1C0416BDB8AF883D3C2954BD19941E3DD407ADBC3423E33CB1C589BCC53D373D73DC4D3C84E35F3D7B6CA03C710D6A3C9A4C46BB2934B1BC78052A3DCB0D02BDCFA194BDCC6F863D75CB84BCF30E6F3CE998973C1437993BE454F43D4AE09E3B6F6972BB78ED8CBDCE07863D2E1519BC261D463C91A97EBC51EAAF3CB8DB25BEA3F6F2BCFD84E53B4E679B3DA7FB633CB3A5ED3CF22ED4BCBD13883D33CCA83C5CB1AFBB5A33C7BDA9E1B2BCF8BC813C2116BFBD1B2E363BC779753DB965813D37C1413B0B26053EB31A18BC6670A9BD2A446BBD3D3B0E3C75A29A3D344D3DBD563508BD563B3BBDD2DBAB3D1396673C8D84F43CB017073C4592113DD5A7C23CC096D5BD7DDD223D5DFEC23CA45F6FBD86C1DD3AB7F2A7BD9C2F8A3C0783FFBCFA1F29BE7614CF3C3E73AB3DE7CC3E3DCB65FF3CC3A03DBDDC81013C1B4609BB5E0BDFBD85A6C7BD5781723DEA9A033C2123DFBD8B7C093E101359BC233C35BD924B6C3C02B4843B563A2DBDA416263CAEE191BC9668D6BCC53B263DA39ECC3CBB43CA3C88C39DBC64F942BD15B980BC7C1007BA44A4043E2B65DB3D9B6D1ABD5398643C3311A9BC48AF333D1378E2BC346E263D7A3E6ABD15F94DBB4BD884BD16AD39BE28870FBDBE801EBB5EDAAABD2932483D82E1A63DB710493DA4735F3DDBF8433C67E21EBC7845853CA1D39CBBA5B2243DDC682A3DD94A0DBC0D8B37BD932D513DAF8363BC4EE9883D2DF6AE3D47E17ABC3C8BEF3C6989ADBA339C193B589BCD3B0EBB4BBC9783D83895A4673C8474FDBB23AC623DC0B81FBBFF96F23B1AB1F13CB58A9F3C7D8CA73CACF84F3C7F0551BD724BB0BBAF77533D6A4E9EBD4F87E23D49363FBD3DE5E03D8BD9C4BCD6EF9F3D82081ABEC26FCF3C78BF653D31AA233CCDA7423BCB8CF1BC2D52F4BD32C744BDBF1213BCCA7756BD24F6153DAB61D8BCE790B2BB98EE9ABB0637F13CE190F63B139247BDA6854CBEFB147A3C9EDCB2BBADAED73BE937DC3C8C16403DCDEE633BCB2623BE33F8363C6856873C5828763CA771133EC152323DD4AAA6BD9F14BCBB86BC9C3C4A173C3C5E0B923B721C02BDAC1E31BCE16AB9BD316FAEBC56456FBC685CA5BCDB99A2BDFF287ABD08E59EBD82C2EABBAACB4D3C9231433A1CC3B83CC23BCD3C62D255BBB339013CCD7B08BB02F0953C09A0A3BCA5FCF4B78BD490BCC5B102BD4103173E3FFC1D3D27B0F3BB7E06C03C4F8602BC4EBC0F3D18B4C4BB04370EB9F4816ABA824D9B3C5F61B63CFA1FD8BCA926893A905E08BD95B22ABC8E67AC3C2AD77C3CC584E2BB90008D3C47C699BCFEC5DB3B5154D1BC76773B3B3856403CB0D658BDEEA4C6BC47A84EBB16CFB23BEF5222BCBFD0E73DA6E4DC3BB438993B9A333D3B8B8EBEBB221DC6BC5F9A383D00ACEA3C0339F5BC65F78B3C809BA73C079C6A3BE771D0BA75433B3C5495F4BAE0AE92BC1669413BF69E723B76FD5EBB514191BC3E5A333C1052A3BC67E8F63A00D492BBB9B99EBA6BACD63DEA272DBCCB5774BDE2E066BD656C20BCA887FEBB935E703D54C52C3BD4BD26BE9526603C6606B53D1EADB83C4FCBAF3DA7BC80BBD51CD2BDCDB138BC7B1A91BC534809BC9FAA64BCA2D2ECB93E75613D023880BC82BBAE3B8D8FC7BCBBE8863C7C0BC73C43127CBCCF59B33BEA6E423DC8BB6EB9AAE515BEC7784FBB89DF2DBC4B9D19BDF166863C845379BCF21286BB785B483C954235BD7BC062BDA8D012BD06369ABC384D9FBCE06920BC266B9D3CBB5B5EBCDB2F77BBC17DAF3BD1A7A8BC225CBFBDFC02B6BCB31A2F3C21DD603ED218353C7B0794BE343581BE4D91C9BBA04F903C9798A5BC6772AA3CE0D0A13B67CD143C425EAE3C06EB00395B6B86BDF599DC38945E1A3ED7851ABB34BE1C3DA5A695BC1C1CA8BC6F47A1BBF5F21B3C72D84E3BC1E9BF3CF5E63F3E6F4E8B3B113B93B95DD904BE4E8D133D9F0911BE4CE88DBC0C68F53A64A90F3CED592D3CAB20823E13A07E3D9F607DBC423B6D3D0297B1BC68088ABBFFF03C3D3DC28C3AE2BC1CBBEA318BBB619820BD3BE9A2BC16D0B0BC5A677A383E69AFBA9E6EBB3C4112AABDD3CFAA3B5F2B18BC2BE77FBC5765C23CC859203C6722BFBC0BBB63BC4C2CE63C4479C73C5E55B9BE4CA008BC0C8D30BBF69C813B250867BC03A57EBCFA54623C13A189BC71F70EBC741660BC8DF0E2BC5FD9CEBC1F7663BB872DECBB750501BC6E3A423C9D708DBE2003693C243E34BC0C1DC0BC7B42E6BC925C3EBBA2F8B23CF5F92A3C6585233C743396BCAC59593B82223DBB87A8CCBC9268663B7EB43EBE1928FEBB6B770A3C7C089C3C727B933B2134BC3DD1B0D83963D1BBBEAB18313DB7D4A2BDEFE1083BC417973CBD46933BDDA81DBC0D6CAE3C12F33BBC67EC45BC8EFC53398DC75F3B5A3010BB175ACA3D6E5951BC1E1873BDE636463DAE5B87BC7CB5B03B96C7293CE0F500BDA269253CBE2D853BB0941D3B0EF1993D63448B3CD62D333D0DBBC2BCA7EC07BC60AEA23BEB5180BB13839FBC531A25BDE8EF263C67FF8D3DD2B2323C55D0F9BCCA8FE3BA84243FBCE801693DD9745F3C92A4AFBCD6E90CBC2C52A6BCB7C99BBA4C65D23CDF6BF93B539063BC8C83503C6EEF99BCEF7B7DBDE0ECD23B981069BCE3DDEB3B6116713CFEFA0B3DEC84593BD4DADF3CC58C383C44250BBDFD8989BC3C18B03A3BB6653C32F0B8BA68E929BAAE6AD5BCC35F7DB72C4EFF3CC2C1E1BC3E8B76BB7A78C8BC8E6E8C3CBF808F3C4774E0BC9DC3C0BC12DCD2BB118F6CBB46A941BA602A053CF50A653C22FBCD3C23473CBC670814BD54BD1CBD40E2BEBAABC2F3BC4897CEBC1A4C913B960E2CBD41738BBC3B88DC3C01D31BBCBCD732BB4742143C3AF43ABDB6E6B03C49ED5B3BA8C0C13A3E634ABC8F1DCFBC485544BDAE8EA3BCF71A643DE01B2BBCC0D115BB6D264FBD986682BC9756FABCF55FACBBD88311BD51B305BD08576B3CEC7B0CBD0A4AFCBB5F7BCABC8D8EB5BB6AFD0EBDBDA10BBD52651EBD60DD373BF45AE13BAB0AADBC9EC1ED3BAFC258BCF393343D81D05DBDE3071CBDEBAEE4BC7092C4BD50EFE33B6414F13CC54B5CBC83F1D3B825A39C3D5BF3B4BC4CDBF5BA9D13273DD1CB73BCF284ADBC520C9C3CC75485BC2D5011BD5909BBBCBF47D8BC6D58C8B9BCB9753B2BDEB2BD1D28523B0D11F6BC2EDCBBBCD2B7EFBC4A9665BC7E9529BDEECD583D2A4E22BCF45AE53C4BD78E3B07F62CBCBAC823BDC5573A3B48DB06BCEBBFF4BC05FB1D3C17D7853DA76707BAA83BE4BB37258A3C1E3B4CBC9855C1BC97E59EBC5F90C1BB82E1123C1B7E26BD4652B53CA73BDF3B2D0EFCBBC12C7BBC7B3A883D806376BD6D69CC3CB501E33C03000EBDBFE18E3C7A062A3B9EB891BC9C7028BCE4F030BB122220BD790B10BC43A07DBCECD6CABCFFE11EBC83BDCCBC3486D53C825C873CE70518BDBD574EBA1548C13C064A9D3C7AB7963C0CE886BCED3CDF3BDC94D139D785ECBC94922EBD4A0F4DBB2575113D7FA9B93B3317E9BBA8E8F3BC2142383CB436DDBB3F7B533D8EB816BD10B420BC48FD0DBDE26D1BBBA4153C3CA6B9C9BC83F353BBB00094BC8D44F8BBDA5564BD0CA928BC733988BDC78BB8BCE068A6BC4DED01BC3A912CBC66892EBD3E8727BD1C80E6BCD7C300BD69DA20BD0338303C3721333BE609EB3B8F892B3B672A4BBCD4A4DBBC897FF93B9B2A81BCBFD0A5BBDA92453AD2970FBCBC5654BD0FB759BB6552FFBC41E3B1BB0AB84BBCC46CE63C4E7F363D177AC03CB3EC103C1B5EFCBCA95198BCA804EE3CA752C23BE16052BD52E4E83C8A7A57BD0584273CFF1AE0BCE3D366BCFBCC2A3DA22ACEBD3D535ABDC597013DD2EB94BCEB3C763D89340B3BFBB36CBCDD56443BD77313BE550B44BDE2306EBD1A1C8EBD187DE5BB9D31EBBC50F4423DFB8F0E3CCD249E3BC66C0DBAD99362BCD1621FBD40C60BBD1B808CBDA0A36DBD7AC6D439D5D6033DC31A0CBC85D1103EB535133DAB5592BCD3C6B5BC7105CEBCED72E4BBABD3D93DE85A403D922A283DA3263ABC40A91E3EF0E65D3C6DC2A5BDBDFFD8BC0C82E2BD03A7543CC908C1BC9FE5013CDA34BBBCD0682CBC092B91BDEE55873B0BF6523C6C7D42BC3F8186BC32E507BD2EB3C5BBA7599D3D931C5ABDC5CE1FBA6395A4BAB165FCBDA926D93DDD6D57BDA19E39BBA50001BDA8E68BBD0D3E47BCAB18F8BD9E2A93BCA9D102BD3FDB7F3D2FFC21B9F365683CDD6B303CA991963DB796BD3C22C510BDECC189BB666700BC3C927D3D43B45ABC2EE60C3D9C82F33B223969BE576304BBEF54DA3ACC12E93C7685F8BC0F1F86BBA9FBFF3C618A663D280844BCEB48533D70ADE0BCC5B0E23D9F947BBD52A068BD35DB08BD9FE7C93C65057BBC44E4F13C236DE93C5E30C43BBBB9EC38B7190FBC68C5CABB1E93F33CFB3847BD82FA073DCE4BC53BF4A891BBE2963D3DF73543BD3FA245BD7C826E3C211BD5BB89DEB4BC4BAAF23CC993973C5B7D3F3D0BBEB43C01A110BE659C9E3DCB0426BD09E3A9BCABC09FBC0F57B5BD6E0982BCF76D003D6D17EEBD6E50B5BDD023DBBA714E863B0EA899BD38684CBCC1AA683C1CA90DBEA49054BCB0B010BC157F2F3A5E93FA3C1B361D3C4939A63DF0F643BCE27915BED64117BCD3E1BCBB7E14F83C6A0DA23DD27216BEF7CD303DA9BE03BCB4D2A4BA9532073C5AB99F3B423734BECA1DB13C067934BC9EDAE0BDB358003EB7B515BC1E371BBCE16360BE39B3D0BD0120D3BD2CF52FBE7561D53C4014F63DE368DB3BE39C84BC8F638FBD74F0EC3C31159C3D2EA820BC1FC4263DD7C3E3BB151262BBC6FAF33A40B4DABB3A8F4A3D1C02F23B443A2BBD0587803C28E725BDF7532CBD4C5578BD3A13B7BC7A7594BC61F21B3C351C20BCEE61C83CDBF3F7BBDD415BBC5536E63A887BAFBC1BEED03CBAFA3A3DC304013DF88D823C496CC93C5246593B190ADFBDFA7781BD9FC47CBC25D5BEBDF4ECB6BC681430BDFD390D3E691002BDEF90C0BDDFF2243D20ED36BAB6FB053CC1113BBCC97FBEBD8B01BBBD80D316BEA5A846BD3EBBC9BDD32A85BE439F42BDABD296BD88135B3D9AB4E0BBDD0C973C08871D3CA07D62BD97E513BD21DA783CE2DD883B742B43BD3C69F5BDC1BB4EBE56D3C5BC2CBD33BD9C492C3C042EAEBCA0C6F3BCDF918A3D294C013D702CA03C36BC02BDB0EB99BD8C78003C96D4BC3D894F9FBBC6E5F1BD7D865FBCE19750B91D93BBBDC15DF1BA64EA8BBAD8A2AE3CF9277F3C471AA7BBEC6EF2BDEA8D0A3A7F0F2ABDE70B29BC68C4213CBEC467BB37ADEB3AC0D9DCBCC6398D3C82C432BC9DF28BBCE25718BC668F05BD5406D53CE5BC35BCCF3E803CC5AE943C76C38C3C9585763BF79456BCDDC50C3DA472793DC0D7E0BC0A1E663C0B95C9BC8DB31CBCD95814396B81973C43F54C3BA878BF3DA741E0BB42F3A6BC99F17D3C4099253D7CCC55BB3052073D57E255BCC5F7CEBCE30E203D75076FBC0E71FD3C307C853A761EDEBCA8C435BB75EFDEBB4B3FAE3B24B273BC684C1B3C25BB303D52F11C3DC94CEFB7EDCC3EBB04DB27BDD05C3A3DA7B24F3CC3EDB53BC6B39EBB3839F4BB62AA22BCE3827BBC08C1E5BCBA2BCBBC5085863C59953DBCDB3D1D3C9D71923C45735A3C8E83383DE172AFBB236DB6BC27AFA5BB873D3FBC6276073CFE07B3BB69FA1E3B9942A6BC4CBB96BC556B813CB1C6C93C7216AD3B519ED03A89B812BBE3F1EEBC5EB30EBA2464973B85B358BD28364EBC05CB403CD030C2BB4B94D9BCE16FE9BBB289ADBA3D40503B5F30BCBCA655383D9EFFA4BCE93EFDBCC49FAA3CDDA6A1BC77B6DDBC9DBC6EBC84A33FBB2546CC3BE108243DC2FE943BAEE18ABB5EA67F3B6F6A00BC4BB604BB9E30E23C04F29C3CE46EF43B788F2BBC6798F63C7E24FBBCA62FDABA478FACBC379319BD4968323DD9B83FBCAFB8DBBB6F3FC2BC0C9C1FBC87E922BC348D92BCEA601B3D982B1D3CD0A214BC7154F83CA18C903BC184E3BB50FAAB3C890FCFBCBD8179B96469203D3F8721BC7C39FCBC67643D3CC6052ABD046DCD3B36BA7F3CAA7C58BD88639C3B05CEB7BCF4BE87BC739A30BC65DF7D3CF64A01BD891DFCBCFFC3043D7382013B04197BBCB83C883C1ADE36BD0E9A49BCC65B533C3450B73C847DF43BD5994CBCB087DCB950905F3C01D903BD00EB6CBB8D98FABC5ECF83BC52E39DBBBFC089BA3A2F9EBC3C7693BB648253BD0AC68FBCDD643BBBDC2CB0BCEBCB8EBA7BBBB53C75F6923C3C50AABC2D70A23E0BD92DBDE9E4EFBC593DE8BB9CC825BDA8F792BC97210BBDC61275BBCFCD443B5E8B8E3CC531C23B363E08BD847B313CA4BF9ABCFF21013DDFE8EBBC18F8C33C9979DA3ADA39EFBBD6A14D3CF3538ABC28555ABC31346FBC1B9E63BD9B725C3CDCF43EBCA9D64ABB40428BBCF3B6D93A5CCC683CF23C893C9BE5F1BC297F303C011A25BA7E6124BD3FAC71BD430C7EBC13BE393DAF79DDBCC7C34ABC7116BCBCEF033E3D751DA63CD052A83C54CE00BC45A911BDD53DF4BCA0031CBCCCB7E8BCA19381BBC02239BB90749A3B942226BB139A60BC5BBA023C54A80FBC28CCB43B2F27CDBCF3E6F03B0B24A2BB5BF1D73B92DE02BD0FFC47BCE32D153CCE6D87BA89C0613A6B0BAF3C400ECFBD8C5054BA3F7310BC62B3673BBDFC52BB917DBDBC0E468EBC2E4002BD9A7F2ABD7166423D8C0A173CF54F78BD3A07363B5BDF2BBCF8FC213DAC499EBC1B6538BE29D230BD7FC9B93CED482ABDAD65923DA0930ABDCAD128BD66ED4ABC79D88DBD020AD23B6739AB392C50013CB0FC3FBCF637B33B09AF04BC5754E53CC8E5743CD8A82C3D3E058B3CADD72FBDA10D5A3BA01485BDCB7079BC7245B23CBA2584BD9817463D4C52DBBCC3DB68BDECCB55BDA88A40BD7877263DE57BAD3CA89788BC16B12ABE986CE9BC44DC57BD8FD1A0BDCB7694BCA9414637831F00BD4A6A83BAD28B8EBCA400CC3BCAFBB9BB16ED01BD140C37BC0E0F3B3CF9E806BD378EB1BCA645E7BB757E1BBDC0ED513BAC4CA6BC767B07BEB5FF143A0145163CB26E17BD0393883DEA09A23D9E9F1ABD35A529BD596ABF3CF08A993C1ED053BBEC1D88BD71F97A3C0CE0773BAEE605397F1476BDA9AC60BB299E0E3DF8E30DBD8B46143D498BE23D795F89BD3541C8BC6DBDA9BD44F8D6BC34B402BE6418B4BD902030BDB31AACBCA54B17BE9EF2D5BD0707893D9BB010BD53B2383D817E4CBEDCEFB4BD7B48D9BD9E71563CAE81ABBD42FBAABD99F783BD86CB123C04580D3D8E2C8E3B30F41DBD786646BDA0153ABD728C4BBC51BEEBBC52EAD7385DE4893EA2E7C4BDF7F20ABDFB0A9D3C9126C23B60BA60BDEFA14D3CB1D1853B707430BCFA391ABCC16EAE3D7BA1783D759D3B3C269DA83C3754D5BB144D253DA371013C5DD5A0BDF338793D741F10BD2575DBBB94A057BC66F952BDE173403CC7E926BCAD3ED23CC0443C3B0AD24FBD31B46EBC68FE443D73DE403CBF04C9BC2957ECBC502C1C3C29C9A73C1402B3BDABF026BB531E2EBD8BD623BEFD02D13DB3DA87BC7A1F1CBD6C7FFFBCFAC806BD50E42DBDE977CC3BE7578D3C530420BE46F961BC6CC370BB684D81BD94091F3D184FF93B4A1B6C3CCE24763CCE9A9FBB6D40BF3DCE06AF3CA9131B3DC0F210BA9B83133DC29CC3BD4E73C13B905933BC7F9A4C3D029A833A20DDE5BCF465633CD8CE7FBC16744CBD4DC635BC9E22F0BCDD5366BDFA3BBDBDE862A43CB679B5BC3341CA3CF37F7EBC88C608BD13B049BC84E8E53B0738B4BC1A9AABBCF08FA63CBC327F3BACDA4FBCF401273E35390FBD0EC4B6BC88B560BE668BADBDDA569BBDEA989E3CF14BC0BC3C3742BCCF300DBDEBE8D83A100F58BDF7EB503DCF150E3D217A433CA761323DF3E88CBB31CB0CBE2F30C33C5AB081BC8FD1A3BCE38429BA16E0D5BC8CD1893B62EC133C70890CBDBDD1163C9F06B33D3DEF6FBD79CEF7BDCB8D70BC943A53BDAE5120BD90D99DBC201C293DD188A43DC717BCBC1573C2BB18C6DCBC335C25BD1AADE33C626DA33C57A5E4BC8C96A1BD4598B3BD55C17ABD9FF926BEC7B77CBC795C83BD888187BDC0E9433D65367DBDF178893D20B723BCE00EE33D5FDCF13C4EE12ABC4995A4BDCC3B8FBB0F0AFAB82D863EBD76BD4EBA02CC76BDFFF2D53DAF87FFBB7427BB3CCA8B8D3C7EF4B9BB078F93BB19AEDD3BB653ED3CC83C2A3D6B1509BE8C7D23BBCF725F3CBF6C863A2225033C6E8F2B3C08A516BD60FF64BEFCD7743B379885BC7170E53D5EDF693EC2AFA13D69C1A93C8B11003C48311E3C0F6F553CFB67533C14BA8BBBDE3465BCC284F0BC28177F3C60304CBCEF3DF03B9B96C0BD4BC28CBDC2D819BED16523BD8673933BE0CC2EBAA4D701BC1C6DF53B9DA9A6BC3BB76EBA1B7B5DBBB951EDBB65FD213CFC51973CFA96FF3B62BF293DB2C8AA3DBCCD27BDEBD12E3DB54814BB799CCBBCAACCFBBBCA28F3BA4AD7A03C9E6A4E3CEFE761BB94FE843B8D17923C81B430BDEDF2703B84E732BCB5ABE6BCE1B5B0BBF5A813BDFEE1833DAF600A3CD24DE0BA8A49EFBC403C69BCB919623BFDBA4CBBDBE4EEBD0C35293CF7BB7CBC3F96E43B03AA323DCF09323CB40D32BC765071BB7D47093D56414C3C4BADEFBC9091483D82C2B93DAE11F3BB8304CA3C2130AFBBB928973CAB4B83B98207DA3CD7B8B23B003990BBA1A301BC6D32E7BB909C363C59D8803C93E924BC887386BC4E873FBCEDF55B3C1674E03B0A0C163CC556DC3C7DD7313DCE7A163CA2F00F3B44B5893CF6198BBC8E8D53BE628F2B3B767BC2BDA2758EBCDECF933D8C456F3CE7087A3C2778E2BCA274D1BBEE94AC3B93539FBC99A16F3A299AAF3C83DC023C52930ABCFF1382BC4060C73A9ED7053C0043773BB220A7BB25AC3ABDF8CD91B9F4110FBE9E60A3BC840D99BC9B74B0BB84D90EBD316D78BC9C0F5DBB1B3C89BBF1EF98BDA61D3A3D8444863BF06FED3B03921ABBF7507DBC18DB3E3DE17B4F3D212D233C8FD5C0BCD9B504BD4A3CF53C057E27BD2AA9313C8BCDBF3EF3B6893A0C05D1BEF5EDB7BE3B6910BC1F44A7BB6DA3F6BB7BD224BC0FF6A8BB6E0BB53A5779ABBB5FC371BC343E72BDF8C3AA3C56E9663E27A99DBA0C684D3D7D94683DFE90A93C93403ABC398A75BC2C9C0C3D3802A7BDD9AE1FBC43860F3DC579783CCCB74BBE38A6FD3AEFFC46BE67C38D3DFA043ABC7E7962BC1C93343B2077A03EE9B6B23D66F1883C06CDA53CF6839F3A23F545BD3670C7BB1459053C077ED83CD3F1EF3B8369463B32D1FF3B9B8490BCCA6A4C3C4DBB573C244315BC95444CBE6E47B53C266AAA3CCE327BBD2A1165BDEC85943B2446313C4089B4BBD034D23B765094BBFB2389BD8904BDBC85629B3AB4FF1C3C4C7D2F3C402B3FBD1C36473C9975943B006C773C3E4C48BC4126543C8CC4D23AF1BB153C27BC283CF5858C3CA8E0953CE431D2BEA047123B216AD2BB55D10B3B2D3D913ADC50FABB3D2656BCBB6E1DBC5CF3BD3C79C61A3CFB2CC03C7BEFF63B29C90CBC6263613B4C7B78BC002983BC42F5F0BB431A21BA344FE7BC9271D53D87CD673CAF68BDBE2075AD3C454AB8BD6699733C50F545BDF60EC737082915BC7333BA3C98BE443C0AE77BBC82D276BCA98F6CBCD2B8F2BCE964383DBA94263D264543BCC7A3C7BBF62F4CBCFC44AABB413ACEBB7C2C943C54C1A3BC2437C8BBEC3F90B7F56E62BCE4671CBC6A3F0EBD918DADBCBCEF1B3C2425FBBC99A2ADBB396728BCADC415BA50A201BC77148BBCBC9E3B3CB256FABBC3050DBAE05E4B3B1DD2383BD121BA3CF0E488BC4FF28DBCF29E5C3CC19412BBE8235FBB44CA27BC21760EBB094FA4BB978EC7BB07FEA6BC3D2A0BBC3BD63F3D4AEAFCBA6A8BAABCF4ADD93A74262DBBE1C0E0BCFEE3153DD53190BC8173FC3A72A4263C00CD64BA6772C6BAB49C2CBC28D723BA411194BC4611253C163F81BCDD2250BBA0926BBBD1F444395E9F05BD3EAFFBBBCF478FBB35060BBC51D745BBE1CC09BBDB58CDBC0900BEBB1629ADBC787A4EBC8D007EBA9C3B4CBC3028E8BB982FE8BAEAA6623C226537BC4109ECBB5006FCBB65552DBD4C328B3B3E75163B561F64BB9F4F8CBBC17649BC3E34C8BBAD77B2BBB41DABBB827C99BC92EA833C4A99B7BC300686BCA6E0883ADA8A96BC07C388BC431D91BB518087BCF24787BC58CF9ABA256AB5BBAD855DBCFCF899BB2A2BB03B151F6B3D9BDEF7BAA38816BCA1F695BB7F1ECA3B8C22EB3B5D12C33C61B4F73CDDC6BBBBA33FB6BCF9C809BC04C58ABB065CCD3BFB4F19BC200BA0BA0F3417BC0CBA693D8B400BBCB73F8ABBE541143C9BE8AABC804EB0BB0A38C3BA5BEA8F3B54E23ABC2ABBDDBA9A5A53BCEE38153C9515C03BE4C70C3C9B33AA3B9FC4B6BB7416EBBCF43486B96BB2A93B012A94BB27176BBC5E315D3B2FEFA8BC562D0CBD1F94AF3BD744B53B5B68D0BB1CBE9BBBBB63D2BCF37B88BCAFE1753CD39E243C08F994BCE9108E3B38A8D03B958B0BBC972BC53AAEFC98BB87EF51BC998816BC3D174EBC4575AD3ADA1B033DB99025BC0284A4BA4C04A0BB67D119BC0129A0BCB66436BCEC8857BB3CD447BCF08F00BCD50BC53D759B4BBB3E9343BC4CFA75BC94C94BBBBCF465BC49FB7A3A398821BC6C98D93B1FB913BCCD783EBC09956E38B696A13CC65A923BCB34FB3BC3BA08BCC796CDBC8DF2253C5BB86EBC167F42BC439925BAFCDA1CBBDDF733BC70C9E9BCE709A73BCCC4B23CBB8E14BC0B59093C06D7E7BB7A040CBC02C6293D201300BDF240A4BB2F4F71BC5ED59BBC425C693C595BBEB91554CABB6DEC153C3808B6BAD7CC51BC50581EBCFCD5BB3C0BBF9D3BA756403AA5F6A0BCF630343C5F3808BCDE74143B26FF2FBC1C8B443CB432EEBA527049BC8F14C53CE25EB8BADBA2F0B9054BD3BA218B6EBB2229CF3C60108AB9924E28BC081C28BC21A2CABBE65977BBEA94E2BCA111AC3BCC3B6CBC14BCFBBC0D9E213CEA27B339249D8FBC92F7B6BC8E0DEBBCE371D53A75D211BD31FA78BC59D5B93B314C32BCB84382BBFD16843C4F2A9DBB42D28DBB13C06C3B7A1F2B3DDEE952BE6B5C273CF914C0BC21C7683D4F129E3A0A633A3C278C8D3A22D2123D9A797DBD4C35D23CDE650ABCD354ACBBD8E52F3CD44496BC71D9EABBE4B86B3B029960BC4D22EA3AA8E0E33CED3BC93B272C0BBC427B74BDBB6CDBBC958BADBD48B05ABCAEB4C33CD9391FBD80A6C5BD8A0999BC76DA053CB59DE4BBA968D5BD82A3BDBDD5AF39BD34F661BD6E1155BEE9D7E23CB340E1BD11E0AEBB846D763CEAB6093CBA9F31BD5532263C74451EBD2F6CB13B739492BC442FA4BCFC4BD43BE86256BCC2EF11BD48B262BC84D3F53C286F98BD9D8AEA39B88ABBBDC840E63C2A7D233B7A9DBD3DD4E5223BFBE1B83B7441BF3CBF149CBBD93723BBE28814BD4B4C963B71D707BDBB3FD03DF4344CBD3B378B3C2E880F3C0D3B17BE147AB7BC02AB023D5111E5BAD24961BCE3C31ABE3B36C13B70406CBD4F9837BDBDFC5CBBFD5CFA3CB95792BD6BD610BEEEE2AEBDD4074DBD0BA08DBC827CC0BD8B666BBDB104DFBC6AE75DBDF3F20B3E577A75BD7A4416BD83FB7D390432933B868B863C7E6F62BD544DF5BCF00817BC1A7F363C30B99EBC336A87BD27232BBEFA7F693C59DAA73CA69549BCF1CB02BCF1EC57BD5ED1B93BF1A285BC9B1F0D3BBD7C293C396A37BD10471A3CD3C8483CB32CA5BA8026F7BC7B0570BDD2DF7C3ABB60BDBD9AE57D3D8F988BBB1740FCBCA6F8FE3B38B816BD543B5BBAFE9F63BC29A4413DC66D3EBC087983BCFEDF0ABDD57C4BBC16BACC3C37948CBDC5C536BC1F533FBAA094543BF16D73BDC6C80CBD3A9D95BCB7D928BCCDA0ED3C1747863D54B222BE7D1ABBBDEC8E7EBCBA3A06BBB51AA8BBB4B5C03C8CC33BBD54E67A3CD7100A3EB1F1863C3E81D43C6A7C63BD93F3293E9099263C4ED7503DECA65CBCF98E6E3C6EC599BC2E52903CF74331BC4A65EABDBF8E3CBB0AF14A3CF7FE5F3DCFAA153DDAC02BBD1E6183BA703B20BDEC03F83B39409A3CA4D0A73B4295A2BCAB1B3DBDBF84BB3BF01C3ABC3D4C7E3C0F12413CB252D6BCA23B8DBC6E0D8DBDE5C6BDBB1B43C43C3934843CEC08EEBA4D7ACEBBE5C685BDD47197380E3D36BDB4440CBDD4B588BD608F853BA0E69E3CEC05193CA4EDC1BDF1E4DCBD10018FBDD24C763CFD01FCBC1A35653C146069BCAA7FEA3D5F5E823BD86F77BD601C173DEA8FBEB9A2E0443CA739553CB479233C25C36F3D1613633D87932DBE38D3363DC87852BDFB24E4BC404E25BDE02D5DBC61BE59BD0848BDBC1687BABD286BA63C4D6A223D5EEF663CD5BFEABAC2750ABD2A4A16BBEE01A2BBAAA5F63C76FD703B9F6BB2BD26DF56BAC555AEBCD4D8E93CEDCB923C1A6CCEBD139945BD70CCFB3C32A5873B49B195BCE876BABC852A913CD503C63CF0E9D23B14850BBD2B12BFBA1AF538BB06D8943C01AFE6BD92E3923C728E9B3C8CCC4FBBCB93F7BB948B77BD5295293E4847E2BDF9DD8C3C37D215BDC8DC093D988E9C3CCB1BDABC55BD00BB173E9BBC6CC440BDF3F3A23C8D796CBD96F8B53B14DD9C3AC05D19BD428314BD642026BC55A253BCBCBF963C39C9C93C0C8359BCF8E7EBBBB4495EBD79DD913C7E5CFFBB2A3B2A3C0C4B943D7ECF6B3C4D7932BD5A4FD9BD131C97BCE2FB04BBE77A85BD3D12B5BD85304DBE32487ABD4EE210BE9279A93C217891BD24658BBCBABA473C0ECAA23C90ACFABCD4DD0D3C9BC5DBBB4EDF0B3B02243C3D63F749BC376230BD92600E3B87F056BDB227E8BC6FBA6FBCA165D9BC5AA06DBC18C99CBDB456063DF7F046BD61AF2BBD2DE1363CC63784BDD37C233B87E441BDC0BB15BDE812B13DF50CB7BC9F2684BDE1EC51BD44EA94BBA22B193DEA4D0DBCEE5068BE191D9C3DC6DE953D4208803CCDEB453D5780A1BD5F55B23BF01BA1BDE2FF383C414621BC61D52CBD3465953CB44594BD9074CEBD90C30EBDE06A3E3DD0B5C2BC3AC68ABB7E109FBCAAAC17BD5ED0263D7E9CB6BC40618E3DE83AFABB6C6FAFBB74DADEBB0A4638BD88BDA5BC972DDABC3CB3443B8A76303DF25206BC4F7707BED195733D212E1FBB9E27A7BD4EEF10BC524F40BCE913003C3884A63CB7A1B3BBF3FF34BD03BE4A3D667A9FBBD31810BDA4C3E43C202B01BDBC9E04BD18E9B13C017083BDFA3D313DA3C8463C3A37E0BCEB97D03C2E864C3D5B7150BBB44EF1BC87B9C4BCF351133D524B05BD0601793C358F2ABC085D6B3DFF131EBE82C3CFBAC5E0ABBC345FEDBBA276DD3CDEE8C6BC3163D2BB9AA981BD57BABE3B82A3C8BC669E2BBEF977153EE04F603D109BDABCE065E3BC9E4A853C4480B03C469F06BC4872243ED2B48DBC633CA5BB0E69D9BCB01F753D24A09ABC0B808EBCE1A31EBD63F6DBBC743126BD0EFEDE3C7140E53C888D70BDAC9062BB50A004BD27BE6C3DC9C3BEBB1990383DDB3472BC239E47BCE325EE3C62D63FBD461E06BCEFC6E4390DE2163C1822263CF034263D3CF0ACBC32CB933CE12C323B1A86C6BC8ADC22BD4E67B3BCC8AE28BD9511043DEF7A503CECFF8EBDE8AFE33D63C315BDB59218BD4B051FBD66D1DBBCACCBC6BC9CCDFCBB13A5DBBBCBE562BD68270ABD21A620BBCFF1873C0EC6A53DBD3AFBBC59219A3D5C76903C589C53BCA992853D5EFBA43D0F4B023B3753B13B0C80703B988C9F3DD41FD8BA6095CE3D36DF3F3D81EE2F3D05C2E9B85EC4453D01DA94BC1DD3A93BA8550ABCACC3633CBE522EBDE35241BEFB97923C6A9426BB4EF8D03C3E1A003BAAECAD3C712BC83C9A6DADBCE781613D90C674BDE6BA073D3298FDBC7FEFDE3C2CEF4ABC6782FBBDBC2CA73CA052D7BCDC08593BA05D0DBD5D9D0CBC16A163BD15C1FCBBBF1931BB4645D1BB1077CF3B60C8C5BA1B097BBCCFF8FABDEB6AA3BDBD8F4C3D5F040DBC9F9D13BDA196543C78A7B63CF10C17BD2088BEBBEADCECBC45FE843D5CAC48BD3B7C42BC5E1829BCB8F2BEBCE8EDF73B37AA9FBB18D2073BE65B8DBCAB0E643B3EE1143C846685BC6F56DF3D64A79C3D0092B63C8AFC2E3C95207EBDDF115CBC9CAEEF3B6DFAC43B24BAE33C11A230BCAA3D5C3C01B43C3CD48B5C3C4A44EBBB945C473B45C08CBDEF1E123B797BDBBC62460FBB07F16ABC38B3B53CD07E88BB11F3C4BC02AA1DBCCEC106BDC2209ABC4A432CBCBDE2373D55D54E3C3CF36FBB89AE05BBED44013CF36951BD8F25623D24A27F3BF2AC933B2500D83C5768A23C0F7AB73BC4E4CABB6A530ABCB99C0FBD0AD14B3BFE2205BC1EC7C0BB154808B9A650F6BCD6A0883D10BE96BACE13AB3BA011173BB12BC83B772F8ABBA35CC8BC06BE6FBB8362423D46ADC33C0CCEBD3BC2AD553DED85573C8A0D6E3CF4CEDFBB14DF1FBC87FFAC3B2F4949BBAF6620BD0FD11E3AD34CF5BB3E85633C1D4ABFBC76B2FFBB8CFA5CBCD4A619BD7CC67ABC438F8DBCF32DC5BC4F6E0CBDFF21FF3C395BCCBB044595BC5153ADBC84F57C3CA3DCBDBBEDF832BB9822E93B4755C4BB915C0D3B5B0DF83A1030F73AF611293E1134B63B0018CA3B26D053BCEF83443D15469FBC1974B2BB56C6723B62FBE93CF2E5283D448F723C6C2120BD5F23B33BF0BD14BCA70505BD8FFFBF3B8FE49D3D8C6E9ABC709DA4BC489DF33C41D08FBBECCC09BC7B5A893CC9728ABC00F78E3C5CC768BCFA1B49BC3394FE3C4BC49FBCB519F8BCDB52AF3B4EAC433C7A046FBD8025BABBE673D5BC4F96DABC6678223C462798BCBC1CFDBAC683D73CCBEB853CDE4DDE3A7210903C77E8BDBCD4CA22BD3719023C52D1F83B7698D43B320A49BAA3342F3CC085153CB86EC63B058770BB021F35BDB19CE3BB9AD523BCEDFBA4BC9C056CBC8A226ABD9AA7083D407F8C3DB667673B9D3DA6BC539DF839491CA3BBDE35A03C70FA48BB13DB953B50160C3E601733BCDDC704BC87D9F6BC8626A9BC238E7CBBDC088F3CB631C4BC6F6A12BC6A7BD2BB7F0DC2BBA5A038BD37591C3D57B7B3BB720832BCF85CF03B0EB337BCD146633E646682BBEA66B4BA0B266D3BD11685BC0F7E0FBD505BE2BCA93ED53C5BDF80BC7A1434BBBB96193A23989DBC22B603BB895A4E3D62C6C1BCA18695BCAA133FBC32E0643C59FFCDBCA73B613CF357253CBEDE07BD5B8C18BC8B6CC8BC37A77DBC5419EABCAEEDD33C57BCEABCF2A8A9BB6CAAB6BC6E6B88BC250AC2BC70EE25BC50B092BCB50F69BB488730BCE460CEBCA6B7803BCDCED8BA844CCD3B4CD617BD1576B43C92BD6FBCB64E63BCA72D28BC937B323DDAEA82BBE3FAF13C974B7FBC0364083BF9A1503DCB77523C5376243B05C406BCA37DC8BCAF4D893DE44D38BC7C5C983D73C4CF3CAF30F13CED1455BB59D696BC4A63993C608383BC06BD56BCF4E50CBD92E82B3EA5EAB6BCF1A10ABD5CB0143DB313153C25E3173D790515BBED26F7BAE71925BDB0269ABB8A2949BC08A40BBEBFD9283CA52D4F3CBEFAC7BC147D00BE4D76E8BB8DD0E1BC3417A8BAC74BD53BF95920BD8E8950BC6FF2B3BC29986D3C0EDBB6BCC2CD5DBC2B74503DF31F57BC05E7C6BD9B679B3CCE5B89BC68DF45BCC75A4ABC255A7EBD76692ABD753C82BCA3C3143C37DCA4BBE8CBCBBCF974C1BC960DE03B8CFFFFBC426B7FBCA31B40BDADA9993DB823973C1E11503D58C74FBC811437BCB9A76ABC92E58B3D4C2CB63BD5A0B0BC9712783DE6E9D0BAA3B965BD880262BCC0BC2DBD512FC2BD26B9173B3FF51BBD4AA380BB44C48BBD0BCBEA3C5EDA923DA562AEBB0EAF40BD74987ABD946549BD1E96B4BC4F0D713C2EA63DBD43D8F63C252D19BD3D77833D16D7DBBB7FA002BDDD05A0BBD56808BDEBCDC9BB3A2A8F3C94AD3EBC002ECB3CC34C49BCDD19C8BDCC249ABB94DE033D8DE04B3CD97113BDC71144BC7A09213C61D34FBD4AB24D3CD3CB443D4DF3E93C70390CB9379DE63C6A2D0BBD2A7F853CBD66693CC01F793A96DE85BC6894BFBCC741ABBDE88B2C3DE6CF9ABCABDFA7BC7B3F88BAFE9A01BDD75B5F3D1E71053D21C6723ADB02E0BAD4D668BD3A9C65BBCA19FA3C764A08BDCA5059BD9CEF0DBD6A7FC33BC094B43DA9FCDA3DB46283BC779324BD405CB03CBEB8EB3C63A0823C46C740BD9E4FE3BB4012CB3C1E65B1BB36FE7B3CB1EFD4BC388F2D3D9B86EEBDAD92073CF2991D3CDBE427BC722C13BC199483BC0D29193CBA72243C7226643C1A49E6BC51F2E6BC1DA86EBD20466E3D7CBBD9BA4F12C1BC8F466EBB18CD273DF054213CB5010D3E6506FD3B1F4E57BC76AC57BD914CA1BD03DCC23BBF8AFFBCDCAF86BD3914E5BCC3A871BD26248F3CFEFEA9BCCD1B80BD5392313CBF6EED3B552C943D40FAEF3DA402B3BD9ECE623B91D414BC7AA9BB3C9AA0523CB121C53CB5528FBCE7792B3DA6E0E13C8D43333CBCDF173CBC1710B9F17D3E3C695AEDBCBF760D3D3CB0853CC4974E3C5F0711BC601C22BBDD4A47BE7CF0A8BD8B41563C079FF3BC57451D3D6B436C3D429E74BC98BA69BDF2C70F3B7AF398BDDA6E1C3E6D05FA3C181C00BCD3BF7A3DD815433E4983EBBB771A9FBDD458413C171C8D3D6D5B0F3AAF73D03C18F509BDB5CB28BC78DC753C38BFFE3CDC7D893D5E9C783D6DEE8A3DA3692E3D61E6A03D9FDA76BD2273A33DF421093BDD246DBB657823BDF42EA0BDBA91CABC88F86DBB89A967BB0C8CB23C70A8E93BA480943CD0B2213C87AAE33C3FBC56BD893C203DDEC4F83CA514F73DBAEF9BB9A8C1B4BD6196263B8DAD9CBDFAE6BA3CABBF4DBDB248EFB9D1C565BCF341F13BFD66973C3F47A83D9B59D7BB521829BC4EF0643CB35AD3BDD77ECD3C936BCA3B6FF2403C80232CBDFC308FBBF08EEAB9222DA3BCB0D73DBB563700BD62E2663DB1C75C3DA26A06BC206DFBBB07752ABCFBE290BB7FDC03BC78E7733C44C7E9BC6CB6333B73BF4B3CBA1378BD721DD6BCFEA908BDEA2C39BB9CEC6E3CDB2507BDEB16C5BB2CA3963B313E053B72B98A3CF90AB8BC6D4E1E3C84C0EA3CF64DAE3CA6C6CF3C7CA4103C82E9233DEDD6633BBC8C6EBC6E8546393580633BF607B53CE02B29BCA272A83C37D417BC0C7F0DBCD0FFBBBC9F8F5DBCA4C99D3DC9D9BFBBE870A3BDEA8821BCD121AEBB6AD9F6BCA0FA8D3D4C6D023DE7B7913968FCDC3C4EB08C3CBCCDBA3B3AC0EEBB881D6CBC79B98E3BA4D4D63B4777C0BB254E5CBA058231BBF45106BC59D237BDAFC35DBB5F84913B3F3EA03C408EC63BF111CC3B33D374BC24CF01BB395E52BCF899B43BBECF003C033E27BC64049E3C73AB6B3CDD68AE3AA6D671BC447C45BB46FED0BBED2C5ABD6C29CCBC8745E43BE04B17BCAA6CB13A89D69F3C752A3CBB092393BCAF0D7EBC69282ABD38C4443CE26591BCD6D25EBCE8F9E5BB22F78CBCCCCA41BCDC89D13B4C451CBC8AD595BC553F163BFA8886BBFEFC49BC11F4B5B9C6D3B93B2425B23D2F10B13C7C97233CD0B97DBA3829BB3DAC48A5BC8B28D03CFC0F1A3C7A116BBC191D0F3CEB8A953CCF9981BC41C26DBC0E14BABB8AE2E13B0D69343C2E4EAC3D36AD3A3CF98B943B3C68573C3B84D1BBD14A81BB85FF923CDB6665BC223CDFBBA8B4DA3BF156F4BBABA3163DA03E893C738ABFBCD4DF923B9795333B52DA09BDF3ED23BC7077CFBB6B855DBCCCBC67BCBA35133A3E3AB1BCF70BFABC27218D3C257AC23C3DA04BBC7F0E4F3CF6FBCDBB41B5A93B40D972BB80138C3C93EFD23BC632EF3CEAFE3B3C4697B63B8CDA5939A077B6BC541BE4BB632DE4B9AE1500BBA67C4E3CC3F0923D9566863C7D5EB03CB2A925BC478B33BCE1920D3B41D216BB225DC63C254CE83C65092ABC6418D13DF3CB9FBCE19E87BCC68C46BB4BD28FBCA2910BBB54EB6D3CB1BFCABCD8DE983BC929013C56E3F7BB9EC127BDED17E53B0A594B3BDC99593CF36726BCC025C5BC5427C63CB38F37BB3E0C103B8D75303CE22D2DBC770D00BD29FD0A3AABE7703B034AEC3C95A5B93B107A653C251987BC7DBF863B3C3C3D3DC42B8EBC38EE6CBB63180ABDA73D68BBD4395B3D34841BBCB28852BD54F6763BF8826A3B3D9EA5BCC514A53CB2FE183DCB8C793C59F11ABC5FE8B9BB47DF003C820D4CBC18BD89BCD1F0BFBB2F32223C2CA7D7BBFB147BBC5672513D7FB45A3C4641183C8BE7513C9BAC4DBCEB56BC3CF3E1A239BFDE583CBEB25ABC1C3237BC04AE70BC2E41ACBC03B0E33B0729303B6991D9BB0643123A6ED00C3C449750BC1A9314BD3BC8BD3C3CC99FBBE5AE82BCDE50733C583146BC055FB6BC9FB9093CE05D32BACA7A48BCDB0E873CD7EDF9BAECA447BC0BDF283C9585553BDCC1B13CC864C3BC237E56BD1BF19B3B2B3D6ABC1F85DEBB9B23B3BB8AD29EBBCE82D43C88263ABE962CEFBAB1401B3AFFD98CBDBC9A5E3D5BA31EBDC15B1FBFA3B448BCBFC1D33C2BA2A83C5DC2C93CAC683ABCC6A34EBBE09E673E41A687BC6FE512BD4561863A51FE723CB56EB63C0DF1023E89EDC13C6F0A6D3BD466C73C7EFE203C1E02173B6E2716BC29527A3CBE6B263CF3561ABC32C9863C583B103BB7578F3B2871D6BC0616943D03AF193C7940CC3BF8532B3D5DF3CABB28D4B93C9F4312BC841FBE3BADF6903BF917923C03459CBA637B54BC56B5E03B880063BCC0278D3B48A7333DA8004A3A60C8D33CA2F7CFBDE4F6793C316D18BB9F25D53B26ADB9BB7251123CE67DFBBB9F5786BDA67EC93CB1EEAE3C7DE130BCA1F4ECBD51C3EE3BF6AE393C41A11F3D1FBDDABC07152ABC7558F03C8C01653D0C6A0FBD975D853967DEDCBB2C9A41BD800DB03C90DB883C37FB82BC17F030BB30FA58BCE0B72BBA160E543B288518BD9BF2C53B340388BC915549BC27CABF3B3C56B5BB0AE6753DBDF9EDBB4FF2073CF3929EBD9C7F733CA485EC3BCF0D2DBD7FA3A53CD7CA40BE75978B3C92FDBE3D3EB0653CECDF1D3EAE2E1ABB5D0EF6BDAFD35ABC7BDA88BC073DA9B95C72E1BC557C223CE129A53DAE9717BC9590323B2CEF37BC368A233B2B1E18BD9BAD593B704D713C9F85233D728D3A3D1DD917BE48B8133DDCFBD1BC3D647B3CA9C4743B43E3403CD744153CA09284BB407D5BBD2A361DBDE5A8E5BCE17E32BC349D963B7A82FABB695FAF3D1AD33DBDE31BACBA49D59E3C3599A33A47DC82BE6E86CABCBA9B6E3B4D5B2E3E0D980EBC23C465BEDBED59BED22D71BC962BDA3CEDA564BB17308FBB975C693B18D02DBC31DA4E3B8F2AAE3BC15BE0BC1725D93BD67341BE8C035C3BED21FCBC255E96BDAE7F923CB9FF04BC882DA2BB7DDB243C0BE5223B9DBDC6BDF1703E3C4F8EBCBBF8460DBEBA801FBB82AC40BE39EEDEBC5C53E6BC3AF356BB32938B3CFC8D563E8EA9953DCA534F3C3C60E13C7E6BFB3BC1C46FBDABE316BD959BC0BB68259A3AA41E3EBC56CF92BC2B0343BC2505C6BBA54B5EBB34480D3C1DD288BCBD1F443C5CFBB4BCE3D2BEBC6D97D33CA9ADB2BC8F4F89BCB11F36BC01311E3C3470F83CC95C113D878634BDF7B5A93CE85013BB67302ABB6FFE66BB3576AA3C3DFCE43B91C188BC762C8BB9684C15BC4EB891BC7D0BE5BCF6262ABC104BAFBC3BC8CFBC71B70ABD5F4C553EF50C78BACA9FFEBB16C14C3C7676653C8E95B33C71F3863B6E6F073C5CCA0F3D495F89BC44F6293CB658F93BB1F03CBC0E60013B731DA6BEDCBACFBC300F50BCDA92573CB617F83B191993BE264836BC993DFFBE837101BE210CC43CBC3A133C206B9C3C3F8DC6BC6B98ADBC04FADEBC5905833C0DA55FBCEC67713C20E613BC10244F3CE433B43C8B1BA63B6B06B23D240C8A3C679C243CCD0D97BD786B863B4C3D803DE20576BC7B4E58BCAE76EBBC29CD9DBD1B15A53CDD0BCCBBA71A353CB1219CBB9A6F533B638E12BECABEC63C00AF4CBA2D7866BDFFA6CE3B6F4353BCA07A03BE9186943CECE8DC3C3F9DC7BB90606DBBEDBB303C52DB22BEE24EE93C846E393CFACB2BBD2EABEABDB3CD2ABE401F81BC8774433D889FA1BCA5BAFCBAB9C01D3DE7931ABE7BBE8B3B70A6983C7F8C68BC752632BC39539F3A7FF5963A67B7243D09E3523E8B8F94BD865B063C476C203DACD4D2BCCED4943CE462873B817E29BECF69F53BAE81793D7DD9FF3C035498BC997C8CBD8A8D92BCCB63263A0E6D0DBDD4171A3DE8069F3D533D26BD361C233CCC41A33D3BBBB83C980A2C3BF7036A3C3C9C08BDA6345EBCFF4D6EBD3A6A243D8C15233DDF9C0F3C847AF53C9E3552BC4ACD323DD4E933BC3DA9503CC65FA4BD0B3901BC9A67D8BC74A4963D61B854BDEABF4EBC36C40BBDD628153C55C38BBCDD9212BE39C28ABC894280BE671620BBFC6003BCD46FC4BD3E86953BE00C203E54F534BE002B803C3D94863CC7EBFF3C04285C3AC22A943D3B58653CBB85B339CC0A023C4A1ED93B66BA8ABEBA0ECA3DA1E1ACBBA9BA0E3DEE1D483D1A87BF3C0B15583C05D1D03C40F8303C8CD7863D89FF0CBDC45D8A3D2638F3BCB52E54BC536B08BDBC50793C14D2B1BC5FB3CB3D5835BCBB9D153B3D3AAFA3BD16C0403CD919C6BB129A853B2D6DACBDB71194BD36159EBC0C69F0BC20200F3D471F2C3CBB55BFBD6B85B8BD84D61F3BC089F2BA7B4CEF3A5914883C9D194CBE09D88BBD0735133D9D8D4B3C4A9318BD0FAE5D3D424E0B3E375FCD3B489CFFBC7CD1033EFF473E3DEE078A3C67E41F3D89625A3B981B99BDD93E32BC3DD20B3C0AD91BBD6EB078BDE2D19CBC82D5C53AF0CA3DBDD55CA4BC8C6845BC8B0792BCB27A6E3C0B56A7BD5FBF9CBC89F64B3DB4EAECBCCF36033969911DBD105216BE9D6E5ABD366655BB6F9C6CBD85540D3D0E9173BB706FB3BC518A7C3C6F4670BB9896703CEB4012BE6037053E326F01BDF17D95BC8F041ABEFA96CA3AC6E3C2BC9C20A93C3937393CA596903C5D86053D9457A33BE0ACC3BD702C993C6F35763D94E997BB4401A33BBC42A1BD34C8E7B9D92FF93C81EDB1BDF1E9D1BC6CB31CBDF76FAE3D6A25963D17DE56BDA24E30BDB252BF3DE9009BBC6259153D957B98BCB752F93D0DFE5F3C86A408BA0C7715BCAAAF843DEB2A08BD583E4B3DB274693C09AEC93C64CC9EBD356AF83DAFC9523DF6E23ABDECBDC8BCAFBCB03C4D5382BA0C28203DDB2932BC5A2759BD20D2AE3A8A1C793C4E42D63D66BD5C3B3C93673D7ACC7F3A197E2ABAD25EF6BC84A273BD3AC5BCBCB26EC83D920F023C1E218FBB65DECDBDAB6E583B97B3FDBCEF3682BCDF7F823C2C684F3DFC4466BD33CE103C020E803D6530083DEEAFD93CE06506BDCAB756BE0C53003D33B3E43BD80891BD0481CA3E32D9D5BBA5A3153EB733EEBCF4BF00BCC242F5BC49E024BC52FFC73C6879173DF724EEBB3EA54E3B780E2B3D775991BD4E96C3BCC6BB47BC60AC453D38E12C3D7355EABD96662A3CC986093D4BC518BC1F8BDA3BE5B91E3D705784BD019A9CBC850D1DBD6FA3C73DEAFD073D39D14FBE94D4543DF90DD53BDA9A32BD1FA20C3C3DCC7EBD91FAACBC7EBE42BE97E228BD3D06883C1F3FC63C460B31BC5638E9BA06843F3D968111BD6ABFCFBBCDAC4BBCFA291D3BF406583DB31F18BE8737C43DDF3D143DCF35473C02F352BCDA0387BD7991933A3F16AA3C7D862FBD933060BE5BE9FCBC7E4E093EE3BB5ABD79B647BDB05FC33B8DA3E8BC9C66273D04EB113D4984CFBBC9B8C73D934ABF3C16DB72BC5B03873D9D5612BD506B2ABD2177C33BCC3EEDBB7251DE387C390EBCCA0147BD946F233C5573393E57FA2C3D9A6E73BDC2E88ABD10EF94BD6DFC833DE29BBBBC0062983BBB254A3D8A470F3D1F258EBCBD3C4B3C994A08BE14EC3ABDDE43D9BB3E139B3C453209BE52BE15BEC37EABBAFDE02BBD2A0EC73C71441E3D745D053CB8F4F8BC8F15B4BD12FA98BC9727EA3C8300B0BC2A7141BE70AF84BC19140BBD905D713D436E4F3DCDC44D3A6A1C7FBC8B83BEBC94ECA43CCB0F14BD37BAAE38642273BD737603BEF6C5103CE95F0EBD1543213BDD2FACBC4898103D12F646BDEB6737BCC3FE9ABB909E21BA41A8293DD61F943EFFA7033DFE9282BC2985BDBD7FFD833D9AB10DBED34B133C6B65533DAD063C3D209095BD23A25A3EDAD0BCBDA3C9653D307505BD24FB003DF0BB97BC2D07EABB904ED9BCE53F96BDD7391CBE9188A63D2AD1213A0F2E85BCC8B98F3E771BA03E658F493BDE0710B806FB31BD9EE6FF3C659CD1BDE3F93A3A467D9A3B52AA1BBBB582343D1A41993B283C013C2E92623CBCAE16BDB6674B3CC6CC303D3C9E7ABEB8514DBD7EAE9BBD54FE683C95CC103A1FF623BD0A8C8ABD37113BBD08BB1F3E49E7363D95A013BD026696BD4EF3A8BD0B859C3C44BF24BED4B72ABDDC381A3BB146CD3C7176393CBB7F8ABE4C3BA6BD78EA91BB08EDA13D54DB7FBDB0711ABD11FA0CBDA39AC83CE510F6BDA85FED3C48AC273E1B8A3E3DABB476BECE29443CB966973C4A58F63CEFB6CBBA79953B3D0665A5BD32D38F3BAFFF883DCE0E953D3BCE8E3D6E81DA3D71B5FE3A918B593CCB0C3FBC9DD308BE01623ABE3541C0BB6604BABB703FFEBCBADE953C463B873D262246BD9998A6BC9BFDBC3BACEBA23DBF9269BC83F2C6BC7DBA043E9036E0BC8A97CFBB8B2416BC264EA13DF6B2B93C80B78C3D0278063E3719D73CDC8681BDBCED923C3A857FBCA52DFB3B7B1F003E6D4F9A3D19F06C3D298ADF3C2BBFEF3CF7161DBCD477FDBDBA90193B5E6B89BD2968173D0A7F28BD09287F3D422B6ABA5E5B053C1837A4BE1A8091BD26C1E43BFC0D4C3CCA1E9E3CB6D40BBE79B5013C93C53ABDCDB79F3C4E2ECC3C5482FE3C571EC83C3BE49A3BA23D433CD4D9103D5FD9A03B42900E3D48D6AE3C11136A3D9D63B7BDD2B0403CCC4DC0BD69A9B53C5DA9173CE79F44BDB48F25BC5BC690BD6321F03CC1D8AF3C7AD0933D1B9B2FBC9A32ECBB4B71CEBB0A4985BC8839023E730993BB6F241F3D748EB33C22E621BE03D004BD9CE7083C51D89D3D629A8DBCD5062D3CFFE00C3C56C39D3D93B4033D112FF9BDB1FA383D29B03ABC13B6DF3BC4CDA93CDE903E3BAFBA1F3CEAC28C3C020BF0BDE05DBDBAD868713D1D477B3DD6DF02BDFF2005BD6FEB11BC44450B3BFD37E8BC5E128CBD1ED4ADBDBA0A0FBC4CECF0BA8206143C2BF7B13CED4A3D3D1C522C3D3CCC333B91EDDBBC72E6543D66EECFBDD582293D24F361BD914B14BD033A58BDCBFC0F3C3ECE7C3C263FD93D33568E3C34C07E3DF4A8E93B5F3D333B7978DDBCFE5E8ABC7AA9CE3D1E4D1EBE86674A3CB342443D6C27CB3BFD55F7BBFBBCA43DCF932A3D5F5394BC453124BD304E153D6F4E403ECDACE7BDA010FE3C240BEE3C81BDD43C223DA3BCE5C4343DE03E5B3D5AE7E53BDEA6DDBD368B3EBDA86A11BE965CA4BB449D903C1D1B21B9D9CDEEBC9ED53D3CD8EDACBDCADC9EBB06A7C03B5FE521BD299F55BC4D6F413CC411FE3BA25B82BDFB3237BDA760D13B4AF5CFBD2CE87CBABEB9923CA49A9CBDB17B8EBC6FD2703B7D0ADCBBB5492E3C34BEEA3CE4760F3CAF68F1BD946FD3BC0BD4FA3C4E0355BDAE2D483DC18F263B758DF2BB380CDBBCFE8DA13DE02ABE3C5CD9CE3C7DA3A43BFF36533DF0BB6ABDC602203C4F4258BC8688B5BD438C8FBDBD6496BB5B2DBCBBFC5F87BD94B0C7BC8848CBBC5D6F18BB947E9CBC98B9C6BC868508BD08F49ABD6CBFDFBB3D2005BDF7CA99BB0895753D1FBB6A3D442303BC5747E2BC5F4383BCFCB956BCA44B603C81CA813D838FB5BC07470A3D981A05BE47FF36BEF9230EBD016E8A3B9E3D0A3D37490D3D8398D739DB25233DE80750BCD3EF143DAC0EC83D35B94B3D8BAC20BE0B444B3C47912DBC539076B9D5A235BC9021A7BB1299763CEEBE0A3D0924133DFE82AEBD5CB4E83C36D70D3B60A76ABC1C4290BDB93909BEC8C7193DDFBF43BAAEB9E3BD9C8D3F3D019389BD331BE1BC88CD11BC29D65EBBAC77D1BD6A5820BD823B983C911A663D9DCFC63CAFB9AB3C9A5B95BE8ED234BD38583BBCB59290BD64664C3D1B55DBBCD0BD813DADB452BC5456E53CE9CA673A2492B43CD8E5D13DA887913C75DCDDBD496A37B9E6162F3C34AB25BD337B64BC269C38BD976E673D70F0A7BB00FABB3C49003ABD262B03BE8248023CE74C723CFC481E3C5A94573DDDEC5D3C83C58D3CDA7ABF3CC6C455BC84C342BD1552F6BA76A0F03C3122EA3917A1B5BBA642A93DD4E9033E0F0B22B97442BCBCE2778FBC79C4683CBFC56DBBE73BAABC054EE5BC5A42D0BC61416FBC53DE77BCED6DA33DD1AB45BC8C5183BC3263A23CC59F4DBCF6F3D7BB5C3676BD2A95AFBD4E92BFBBBC852ABDE08F04BDEF9D4EBCE6ED1D3E79E2EBBBC00CC4BC23D0BD3CC0B2BFBC2FEC79BC4CEB9C3DC83F9A3C72FCAC3CC84A8ABCA34C893D9D4B91BBA4190CBEEB108C3CFCB4893C150080BDF1CD9DBCD0C70ABD11010BBDDEC38B3C8460303C799F683DE55A02BD914DECBC6224753C0DB1863C596482BD803CD33C15DB2FBD9CBEDABCA394E8BA242E033CCBE703BC0BBC22BDB2F9C7BC6C9C9A3DABC58D3CA68D453DADA496BD422352BDB497CEBD04DA51BD6419213C5E2788BBF49993BD2878F4BC1A05533D35B01ABDDE4AACBC71D90CBDFD2933BD08B7743CC97516BDF7051BBEA42CCBBC5C0EBB3C28B52DBB9C81883C65C485BD2BCE13BD889252BD4183ABBCF01797BC88D715BB017CFBBCA33D883D7D41003CE3C50DBC0C6415BDAC283F3C7638A93B57FBBA3C24EFBA3C4565BA3B1D64413D5ABF33BD4C1721BD5EF4CC3C300BB1BC557F38BD9EE8213B83B31EBC14B45FBE937AC73CBC2B4BBD23F9463C9130773BC938E8BB0ABBA43C898028BBED36D3BC03AAC73CBEA8C4BCC2E7943C688C94BC7C51833C5424783D4BDC453B761C19BCA97551BCBAC654BD17A3BABBA2A84DBCD62D42BD4246893A05F464BC4659E6BCFD2A503E9FB6EE3CD7D478BD0020B63BDC5226BDFD19BABD483F5ABC200410BD40EC31BCE99F2BBB550D1A3D56BC30BE751ECABB3E4B443CA79A4EBC840718BDAB06243DC74ADFBBF3CB993AEFDFCDBDB513BF3B726C95BBDDF6323DE16703BE108E893D7D3B09BCB10D913C4A620E3AB1ED3E3B9F83DE3BC251E1BC9563C5BD54DD8F3C7C7176BC79F7413C9B4CC4BCC7D9333C2C9C5DBDBB2511BD9A2BE4BB6DD6813CB2EC8ABCD158203C0FD2333D7E179EBDC160013C0E5399BC270022BEF5C554BD0A60F4BC958173BCB5F00D3D6024133D6DF49A3D737D5B3C3117063DDD58DF3C567334BD537CCF3D9B2C37BDD395D33C20DDC83B17F3683CB66D353C7CB8C53CD1642C3C11BADDBB47ECF6BDD180BFBCB1F17CBD6D16D6BB1717C43D38B5773D409714BEA3E5A53A652E6FBC5DBAD4BCE1294EBD6CEA0ABE6266153B382914BC7ABD183B638645BCCDAF25BD8AA0EA3BD96AA93C55309D3CB6025FBD047D6BBD38749BBD6897A9BC085556BD4BA8BDBD683C9F3C41D93D3C2F41693B356EF93C864B64BAF087033D50BDC13C8004E83BAF9BCEBC12FAB3BA69A5733B4D6BDC3B3BE4A8BBDE862C3DA755BD3DE7CA333B809B5BBDD22B503C4C3F843CD3EC483C49F4333C0D3BECBB0AE1F1BC40DEC1BD5D56303B043005BD0E1480BBA6663E3B4ACCE8BC047A193C7D6D8B3ED20A313C04D642BCB984083D5A161CBEB1C9463ECC51F93C775E583C29296C3C68AB96BB906770BAA1963EBC2D0707BD3D0F54BEC7CAA83CA65585BC1B22053C6C047D3DA3C4BB3DFEE222BF8809803C684B923C0EBF9E3C267C1DBB766E0D3C6AAC05BC647F333C111EA6BB55E0E1BA78E1E6BC15E74FBCC8BA9CBD4D0AC0BC12A9B63E24E802BDBE55E6BA548A903C0A2AD5BCF4F6CE3CCC6EFCBCF846C93CC9562ABCBE526A3CF3F4DFBBCC1ECFBCF60872BB4C5020BDB162DA3A0629C1BCEEACAA3C10C72DBCBC8FF43D3B75973CE22C23B80B6C7ABC23329EBCC3CDBB3B701802BCEBF3C6BD715D4C3DFC5DC53BD17155BBF82458BE965892BBEE18AB3C971CEEBBD42CC9BC4F062DB99BB0FE3A103BABBEBF3A54BC1C20163BB3D639BBBB4108BDDE9D90BB76DEA1BB72C0293C053375BCDE1A3C3BCD5F203C47E5ACB9E0C7E5BC82BE823CB64015BD141AC2BA1C4C903BB0008DBC3B5CE13D252459BCC1CA1ABD2CE02C3DE612BD3C55CF3FBC167487BDC1CB863C311886BE3A36E6BC055FB2BC4725863C9AE637BDE85F83BCE131A53D8971373D9D60763C8645B2BC1C73B9BCCB5F553B9A94343D828CE33B675DD3BB5905B1BC4AC134BB6C90BABC052FDDBB9ABA813C2733A13C19FD38BC57BC66BEEB03D63CF8354DBCEBE002BF1C76E4BA4F744ABC46CD16BCD587723CBD1E44BEEAA059BD4F9BDCBC51188BBC54798A3B51C01A3B0222EA3C285FA1BC0353553C053242BBA68FE5BB9CA3D2BDF43532BD86BD9BB9B2B1843E552C873B631E9ABE1B4D96BE1818A23C6A3E503C7E928DBCD72E5BBB87D4FD3A54178FBB5D4A523CF6F5313CB5A8A9BE2BCABDBA5FED02BE30BABC3B63CA66BB7D159ABD6F94C03CE5E568BC1836523CD6AFEE3CB97EBDBBAF69CBBD9A1D4D3C3E2E5F3B2DAE21BE1E5885BCC45342BE2FE393BC4433C7BCBBF41BBCB36E173C0A2F853E3512A13DD6835ABCE74895B912F9B73C7D550C3D2E3C983C474CD63BF8019B3CA0A35A3CD4E8FBBC18DD44BCDE04A03B9940D8BB14E77BBC2047BABA5C13A63C62AAFC3C9320A2BC873DF2BC64AE89BC99F955BC10BA45BDC7F91FBCCB6653BF70F0EDBBE2653F3D2B27AEBB3FD81CBCEBEF95BBBE5AD63CAE2598BC257A613BBB95DE3BEF02D73C12C628BB3A2743BD3B513CBCC8EA313CE1087B3C9255D0BAD826D6BDEBD56DBE531AF13BEAE0EC3B9D51403CDA64B5BCA7C0703CAE8CB1BA594EAE3A36752B3C78AE66BBFF8A98BC2CF8123DE2309A3C34E092BC22C9F93DD5F42A3C1BF85ABB2FC9C63CCD87923CC8D38EBE618C613C0EBAB13E457813BFCFD186BE5082BC3CDFA4C3BCC6A70FBAB2FD87BC3238A0BB654F383C42A0163E619BA43D1A6E2D3BC11BDEBAD4E1173DB93EE6BBF57090BDDA7E513DD45FA8BDDA35DCBC68DD96BCC37A923CF24E84BB8BC8E03B6C61793D5257633DF98134B885C72A3C578427BA2561A6BCC96F2A3C1F7C0BBDD2B3853CBEAB303CA2FDCB3C4A86E73B960872BE9DF89EBCE0F1203DB7551ABD7815DDBCB161E8BB042290BD60EDFCBDF3B00E3CF0A781BC4E9A873CC6B662BDD123183EEF5F7BBC0B7884BD372A1DBBB9CF45B827E6883DF80F1D3DA41C50BC7CDE0C3C95E08B3BC9B221BE50BD213C7A6A55BE5761FC3CB5D812BC7CBEF8BD210346BD2923B63C034FF4BCB6B8B9BC3582123D155B9FBC79B019BDA4407CBD4093803CC95423BA37A9B6BD063DB9BBDAE0EDBDB74FBB3C2367823D555F8B3DA448263D578D6A3D6E3AA8BA1AF6B7BD7506ABBC050CA8BC8E8C00BD1ABBCFBC39F21CBD4C11CDBCD66ED7BD9B74B8BC2E6B85BD0708083CA5A36D3D88A4C93CB3F9753B19EBAC3C94D60ABBA4ED10BCACC0D2BC5765BBBD2422ADBD9A10A93BEE46F53B7FCD79BD78F73DBCC3BB143CC41259BDF2CC333C51D9F6BBCA691CBC75A4AE3C0A5D15BED13899BC8811983BC49004BEFF79F3BB03ED1EBD19EF4CBD43F7933CDE1BA13BC65CBC3CDE1E1E3CA307ABBCFAC1E0BDA92C93BB384C35BD2B0F77BC5DFFE8BB719D05BE2F0025BDCA84A3BD704004BDF3EF3D3B08E7C93CBD1E84BDEBBF37BD9475BC391B91A3BCD45A70BD99BD2DBD9865103CFD4EB8BD2ABBB73DED3DF53BC610113CCA2C293B86B9963C1C8AF03CAB14CBBB48FCFC3D74439FBD6F0A60BC85B93A3DE18EB4BD4FAEF9BC795A8D3D7D0E3A3CB52BEDBDDE3CF4BD826B32BCB61597BD37E2B33AB1A909BB13B5423DB32EE3BD1D1BF63B32E2CEBC372E32BC01EEBDBDCD54DDBB9F84BDBDA3DF223CC197113D04AD7D38E7B03FBC5827A4BC543B5EBDF7AA79BC2AA005BC4F45973CA0DDB83BC8882C3CC751CDBADA5A2D3D5BD10ABEF124A4BB836905BDDA13393CD22807BD4CE91F3C8E7A35BDBEB47A3C4C3258BC627C9A3CFC884CBCBF7B413BDC761B3C967ED43A75150ABC54E77F3D6B59EDBD198722BE0903F23C5C3A24BCD09F013C63CB023D0C21CF3D69F77D3C7C1603BC441B4DBDF629E6BCDD14813D534ACFBD631AFE3BBAB2A2BB61B396BD6F05CEBBC3C139BCD43F16BC875A34BDCC96C2BD0366B9BD0CBEBFBC1CD3A6BDBA377CBD35EA6C3B3AE268BE42F7833D0C74B43B09CFBB3BCE8500BB4FF407BDEF1A95BD94B558BC683F163C7964B1BD67B800BEE3ECAABD213145BDF4BBF33BB0646D3C524005BEC6470CBB3546363D2A59283C4C1B343B6AE73C3D3BEA00BCDD6AB4BC9E57413CF0C6D83B6DC104BD1B20FF3C85D79FBB3C2401BDDE1AF8BA26E4183B1A02273C4F6E1C3D6985B4BBF23E61BCD94BB43CCC9D84BD39EA303D92025F3D8D82413D50B153BB01A0963DA5B0853BB014EF3DCE21B93CFD2506BCF7BD4CBC3EEC8ABB52DC17BCC698043D79F1943D9065243B9CD0D23C083899BCD9AE7DBDECF721BE0C997EBC69AC4FBD4A9146BD807FE3BCC1FD2F3D741475BCA05DDBBBECDE55BEBE5BA23B1F75A53C01A1113D96C3AE3D2EA6FCBDCABD67BD7C5E343D5E8AB83C4F17153E56B299BC54D62B3CAB352CBD6EDDE63D54815EBD8880B63BBF4E61BDF74B283CC7B3013D394283BDD595BEBC37D925BD26F1FABC887C1BBD9D5D56BCCF8E0B3DA95456BBBBBD3ABC91B6A4BC86BF4EBC5F6C093D81AFB7BC2129A2BBB6594D3DAD44B0BC7335A9BD4D4A8CBC45B93A3C3AECA5BCA22FB5BCC388C7BB5D80353CB2AF2DBC94811E3C0EAE29BD1A9194BDFE1CD53D40E010BDC78A6FBD204CFD3D0508173C3507C73CDE32D23AC86E2CBDF33808BD21A4C13C1272873D0AB3FB3B17170ABC561DADBC23061E3D0726F6BC0954FDBCB63C0BBD687B053B10DE2D3DFE221F3AFBD5403CD2F48FBC06846EBDE5BDFB3B00FBB2BC5115843C47AE85BCDDB771BDA349243C8CC98DBD0D01AEBD3B582B3CF45A203C1555663B7979BF3B8F0B4BBDB970C83B21666C3D5248143DF234CF3C81E1283DC09CC4BE9C30AB3C3F94733DCDFA08BC1468C7BC7281973B5A4512BDAFA6563C0947283DC5359C3C9BDCFDBC57D9E1BCBB0F30BD04E3953B08B8FABBEE280FBDA4F816BDA307583C9C0A81BC3BEBE3BDC2E237BD69CD94BBE1EEA03CFD796A3B8963DDBCE6D8D2BDF9E44BBB03DE5ABB23106DBDC771B93CC861BC3D08889EBBB716E23A1FD4DCBC0275AC3C7B6D8DBC7E775B3C3C7702BDE4B5483CC0396DBDDABAF13DB159CABDA4725F3C9280EDBDDF3539BC0BAA253D25A8F13C00E5EDBCED2303BCB7EC14BDC061D6BDECEA5FBC52F9BEBCF557233BB3AE9ABD51A0F9BC1C58BD3A87FB483CE7AAFB3CB19B1D3BBCAE05BD390D1A3EF0A25D3C0ADBC63C8E93D03B8DF5CBBB05D206BDC174A5BDF00C84BBB5D60ABC314EEF3C94B2003DECA1923DD18C04BD667DA4BC4BED4F3CBEF6BDBE52B7613C8E8E6A3C6F4C00BD6804463BE0D656BDB9D51FBD0115033D0534323C3914F33CDCB2B23B6B24C53DB431FDBC2643983DAB31473C522DA4BD3DA3AE3B16924DBDE864953C114D623D9078C1BB84512BBDCB688DBCE7A502BD703266BD91257D3DEEDF11BD970F9CBDF20DBF3DD150003D055B363DD3B6B33BE742733CDB6A253CE6C73ABDA41E2ABDB02829BCC52E4B3D5FD387BCC5D1A53A2C3EB1BC8E947C3D810E16398AD6B0BC7D78AEBC94D00DBC6E6FF23C3A0BAEBCD7E3FD3C0C3F71BB02595EBC410B4D3E84150FBC1C8D0CBDEEF0C3BD46511EBC123F4A3C459D8A3B4AB1CE3C921692BC1B70E33DEFDB35BC25D14C3E4678F6BCB8B39A3BD9BC44BD"> : tensor<1x1x64x256xf32> %0 = tensor.empty() : tensor<8x2x1x1x32x32xf32> - %res = tensor.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] + %res = linalg.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %0 : tensor<1x1x64x256xf32> -> tensor<8x2x1x1x32x32xf32> %zero = arith.subf %exp, %res : tensor<8x2x1x1x32x32xf32> return %zero : tensor<8x2x1x1x32x32xf32> diff --git a/test/Passes/pack-unpack-propagation.mlir b/test/Passes/pack-unpack-propagation.mlir index 40a0787ab..88d8efde4 100644 --- a/test/Passes/pack-unpack-propagation.mlir +++ b/test/Passes/pack-unpack-propagation.mlir @@ -8,18 +8,18 @@ func.func @matmul_with_relu(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf32>, %arg2: tensor<128x256xf32>) -> tensor<128x256xf32> { %cst = arith.constant 0.000000e+00 : f32 %0 = tensor.empty() : tensor<4x16x32x32xf32> - %pack = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %1 = tensor.empty() : tensor<8x16x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> %2 = tensor.empty() : tensor<4x8x32x32xf32> - %pack_1 = tensor.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> + %pack_1 = linalg.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %pack_0 : tensor<4x16x32x32xf32>, tensor<8x16x32x32xf32>) outs(%pack_1 : tensor<4x8x32x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<4x8x32x32xf32> - %unpack = tensor.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> + %unpack = linalg.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> %4 = linalg.generic {indexing_maps = [#map3], iterator_types = ["parallel", "parallel"]} outs(%unpack : tensor<128x256xf32>) { ^bb0(%out: f32): %5 = arith.maximumf %out, %cst : f32 @@ -37,11 +37,11 @@ func.func @matmul_with_relu(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf3 // CHECK-SAME: %[[ARG1:.+]]: tensor<512x256xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<128x256xf32>) -> tensor<128x256xf32> { // CHECK: %[[BUFF0:.+]] = tensor.empty() : tensor<4x16x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF0]] : tensor<128x512xf32> -> tensor<4x16x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF0]] : tensor<128x512xf32> -> tensor<4x16x32x32xf32> // CHECK: %[[BUFF1:.+]] = tensor.empty() : tensor<8x16x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<512x256xf32> -> tensor<8x16x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<512x256xf32> -> tensor<8x16x32x32xf32> // CHECK: %[[BUFF2:.+]] = tensor.empty() : tensor<4x8x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF2]] : tensor<128x256xf32> -> tensor<4x8x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF2]] : tensor<128x256xf32> -> tensor<4x8x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"] @@ -51,7 +51,7 @@ func.func @matmul_with_relu(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf3 // CHECK-SAME: indexing_maps = [#[[MAP3]]], // CHECK-SMAE: iterator_types = ["parallel", "parallel", "parallel", "parallel"] // CHECK-SAME: outs(%[[VAL]] : tensor<4x8x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL1]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x8x32x32xf32> -> tensor<128x256xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL1]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x8x32x32xf32> -> tensor<128x256xf32> // CHECK: return %[[OUT]] : tensor<128x256xf32> // ----- @@ -63,18 +63,18 @@ func.func @matmul_with_relu(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf3 func.func @matmul_with_add(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf32>, %arg2: tensor<128x256xf32>) -> tensor<128x256xf32> { %0 = tensor.empty() : tensor<4x16x32x32xf32> - %pack = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %1 = tensor.empty() : tensor<8x16x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> %2 = tensor.empty() : tensor<4x8x32x32xf32> - %pack_1 = tensor.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> + %pack_1 = linalg.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %pack_0 : tensor<4x16x32x32xf32>, tensor<8x16x32x32xf32>) outs(%pack_1 : tensor<4x8x32x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<4x8x32x32xf32> - %unpack = tensor.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> + %unpack = linalg.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> %4 = linalg.generic {indexing_maps = [#map3, #map3], iterator_types = ["parallel", "parallel"]} ins(%unpack : tensor<128x256xf32>) outs(%arg2 : tensor<128x256xf32>) { ^bb0(%in: f32, %out: f32): %5 = arith.addf %in, %out : f32 @@ -92,24 +92,24 @@ func.func @matmul_with_add(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf32 // CHECK-SAME: %[[ARG1:.+]]: tensor<512x256xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<128x256xf32>) -> tensor<128x256xf32> { // CHECK: %[[BUFF0:.+]] = tensor.empty() : tensor<4x16x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF0]] : tensor<128x512xf32> -> tensor<4x16x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF0]] : tensor<128x512xf32> -> tensor<4x16x32x32xf32> // CHECK: %[[BUFF1:.+]] = tensor.empty() : tensor<8x16x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<512x256xf32> -> tensor<8x16x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<512x256xf32> -> tensor<8x16x32x32xf32> // CHECK: %[[BUFF2:.+]] = tensor.empty() : tensor<4x8x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF2]] : tensor<128x256xf32> -> tensor<4x8x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF2]] : tensor<128x256xf32> -> tensor<4x8x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"] // CHECK-SAME: ins(%[[PACK0]], %[[PACK1]] : tensor<4x16x32x32xf32>, tensor<8x16x32x32xf32>) // CHECK-SAME: outs(%[[PACK2]] : tensor<4x8x32x32xf32>) // CHECK: %[[BUFF2_2:.+]] = tensor.empty() : tensor<4x8x32x32xf32> -// CHECK: %[[PACK2_2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF2_2]] : tensor<128x256xf32> -> tensor<4x8x32x32xf32> +// CHECK: %[[PACK2_2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUFF2_2]] : tensor<128x256xf32> -> tensor<4x8x32x32xf32> // CHECK: %[[VAL1:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP3]], #[[MAP3]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel"] // CHECK-SAME: ins(%[[VAL]] : tensor<4x8x32x32xf32>) // CHECK-SAME: outs(%[[PACK2_2]] : tensor<4x8x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL1]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x8x32x32xf32> -> tensor<128x256xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL1]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x8x32x32xf32> -> tensor<128x256xf32> // CHECK: return %[[OUT]] : tensor<128x256xf32> // ----- @@ -122,18 +122,18 @@ func.func @matmul_with_add(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf32 func.func @conv_with_relu(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64xf32>, %arg2: tensor<1x56x56x64xf32>) -> tensor<1x56x56x64xf32> { %cst = arith.constant 0.000000e+00 : f32 %0 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %1 = tensor.empty() : tensor<2x2x1x1x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> %2 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack_1 = tensor.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack_1 = linalg.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%pack, %pack_0 : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%pack_1 : tensor<1x2x56x56x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<1x2x56x56x32xf32> - %unpack = tensor.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> + %unpack = linalg.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> %4 = linalg.generic {indexing_maps = [#map3], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} outs(%unpack : tensor<1x56x56x64xf32>) { ^bb0(%out: f32): %5 = arith.maximumf %out, %cst : f32 @@ -151,17 +151,17 @@ func.func @conv_with_relu(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64 // CHECK-SAME: %[[ARG1:.+]]: tensor<1x1x64x64xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<1x56x56x64xf32>) -> tensor<1x56x56x64xf32> { // CHECK: %[[BUFF0:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[BUFF1:.+]] = tensor.empty() : tensor<2x2x1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> // CHECK: %[[BUFF2:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%[[PACK2]] : tensor<1x2x56x56x32xf32>) // CHECK: %[[VAL1:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP3]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel"] // CHECK-SAME: outs(%[[VAL]] : tensor<1x2x56x56x32xf32>) -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> // CHECK: return %[[UNPACK]] : tensor<1x56x56x64xf32> // ----- @@ -173,18 +173,18 @@ func.func @conv_with_relu(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64 func.func @conv_with_add(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64xf32>, %arg2: tensor<1x56x56x64xf32>) -> tensor<1x56x56x64xf32> { %0 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %1 = tensor.empty() : tensor<2x2x1x1x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> %2 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack_1 = tensor.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack_1 = linalg.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%pack, %pack_0 : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%pack_1 : tensor<1x2x56x56x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<1x2x56x56x32xf32> - %unpack = tensor.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> + %unpack = linalg.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> %4 = linalg.generic {indexing_maps = [#map3, #map3], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%unpack : tensor<1x56x56x64xf32>) outs(%arg2 : tensor<1x56x56x64xf32>) { ^bb0(%in: f32, %out: f32): %5 = arith.addf %in, %out : f32 @@ -202,24 +202,24 @@ func.func @conv_with_add(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64x // CHECK-SAME: %[[ARG1:.+]]: tensor<1x1x64x64xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<1x56x56x64xf32>) -> tensor<1x56x56x64xf32> { // CHECK: %[[BUFF0:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[BUFF1:.+]] = tensor.empty() : tensor<2x2x1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> // CHECK: %[[BUFF2:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"] // CHECK-SAME: ins(%[[PACK0]], %[[PACK1]] : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) // CHECK-SAME: outs(%[[PACK2]] : tensor<1x2x56x56x32xf32>) // CHECK: %[[BUFF2_2:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK2_2:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2_2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK2_2:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2_2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[VAL1:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP3]], #[[MAP3]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel"] // CHECK-SAME: ins(%[[VAL]] : tensor<1x2x56x56x32xf32>) // CHECK-SAME: outs(%[[PACK2_2]] : tensor<1x2x56x56x32xf32>) -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> // CHECK: return %[[UNPACK]] : tensor<1x56x56x64xf32> // ----- @@ -232,18 +232,18 @@ func.func @conv_with_add(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64x func.func @conv_with_add_bcast(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64xf32>, %arg2: tensor<1x56x56x64xf32>, %arg3: tensor<64xf32>) -> tensor<1x56x56x64xf32> { %0 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %1 = tensor.empty() : tensor<2x2x1x1x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> %2 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack_1 = tensor.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack_1 = linalg.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%pack, %pack_0 : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%pack_1 : tensor<1x2x56x56x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<1x2x56x56x32xf32> - %unpack = tensor.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> + %unpack = linalg.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> %4 = linalg.generic {indexing_maps = [#map3, #map4, #map3], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%unpack, %arg3 : tensor<1x56x56x64xf32>, tensor<64xf32>) outs(%arg2 : tensor<1x56x56x64xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.addf %in, %in_2 : f32 @@ -263,21 +263,21 @@ func.func @conv_with_add_bcast(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x // CHECK-SAME: %[[ARG2:[a-zA-Z0-9]*]]: tensor<1x56x56x64xf32>, // CHECK-SAME: %[[ARG3:[a-zA-Z0-9]*]]: tensor<64xf32>) -> tensor<1x56x56x64xf32> // CHECK: %[[BUFF0:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[BUFF1:.+]] = tensor.empty() : tensor<2x2x1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> // CHECK: %[[BUFF2:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%[[PACK2]] : tensor<1x2x56x56x32xf32>) // CHECK: %[[BUFF3:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK3:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF3]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK3:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF3]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[EXPAND:.+]] = tensor.expand_shape %[[ARG3]] {{\[}}[0, 1]] output_shape [2, 32] : tensor<64xf32> into tensor<2x32xf32> // CHECK: %[[VAL1:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP3]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel"] // CHECK-SAME: ins(%[[VAL]], %[[EXPAND]] : tensor<1x2x56x56x32xf32>, tensor<2x32xf32>) // CHECK-SAME: outs(%[[PACK3]] : tensor<1x2x56x56x32xf32>) -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> // ----- @@ -289,18 +289,18 @@ func.func @conv_with_add_bcast(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x func.func @conv_with_add_bcast2(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64xf32>, %arg2: tensor<1x56x56x64xf32>, %arg3: tensor<56x64xf32>) -> tensor<1x56x56x64xf32> { %0 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %1 = tensor.empty() : tensor<2x2x1x1x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> %2 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack_1 = tensor.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack_1 = linalg.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%pack, %pack_0 : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%pack_1 : tensor<1x2x56x56x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<1x2x56x56x32xf32> - %unpack = tensor.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> + %unpack = linalg.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> %4 = linalg.generic {indexing_maps = [#map3, #map4, #map3], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} ins(%unpack, %arg3 : tensor<1x56x56x64xf32>, tensor<56x64xf32>) outs(%arg2 : tensor<1x56x56x64xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.addf %in, %in_2 : f32 @@ -320,26 +320,26 @@ func.func @conv_with_add_bcast2(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1 // CHECK-SAME: %[[ARG2:[a-zA-Z0-9]*]]: tensor<1x56x56x64xf32>, // CHECK-SAME: %[[ARG3:[a-zA-Z0-9]*]]: tensor<56x64xf32>) -> tensor<1x56x56x64xf32> // CHECK: %[[BUFF0:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[BUFF1:.+]] = tensor.empty() : tensor<2x2x1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> // CHECK: %[[BUFF2:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"] // CHECK-SAME: ins(%[[PACK0]], %[[PACK1]] : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) // CHECK-SAME: outs(%[[PACK2]] : tensor<1x2x56x56x32xf32>) // CHECK: %[[BUFF4:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK4:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF4]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK4:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF4]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[BUFF3:.+]] = tensor.empty() : tensor<2x56x32xf32> -// CHECK: %[[PACK3:.+]] = tensor.pack %[[ARG3]] outer_dims_perm = [1, 0] inner_dims_pos = [1] inner_tiles = [32] into %[[BUFF3]] : tensor<56x64xf32> -> tensor<2x56x32xf32> +// CHECK: %[[PACK3:.+]] = linalg.pack %[[ARG3]] outer_dims_perm = [1, 0] inner_dims_pos = [1] inner_tiles = [32] into %[[BUFF3]] : tensor<56x64xf32> -> tensor<2x56x32xf32> // CHECK: %[[VAL1:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP3]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel"] // CHECK-SAME: ins(%[[VAL]], %[[PACK3]] : tensor<1x2x56x56x32xf32>, tensor<2x56x32xf32>) // CHECK-SAME: outs(%[[PACK4]] : tensor<1x2x56x56x32xf32>) -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[VAL1]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[ARG2]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> // ----- @@ -351,18 +351,18 @@ func.func @conv_with_add_bcast2(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1 func.func @conv_with_pad(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64xf32>, %arg2: tensor<1x56x56x64xf32>) -> tensor<1x58x58x64xf32> { %cst = arith.constant 0.000000e+00 : f32 %0 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack = tensor.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack = linalg.pack %arg0 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %1 = tensor.empty() : tensor<2x2x1x1x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %1 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> %2 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack_1 = tensor.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack_1 = linalg.pack %arg2 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%pack, %pack_0 : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%pack_1 : tensor<1x2x56x56x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<1x2x56x56x32xf32> - %unpack = tensor.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> + %unpack = linalg.unpack %3 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %arg2 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> %4 = linalg.generic {indexing_maps = [#map3], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} outs(%unpack : tensor<1x56x56x64xf32>) { ^bb0(%out: f32): %5 = arith.maximumf %out, %cst : f32 @@ -384,11 +384,11 @@ func.func @conv_with_pad(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64x // CHECK-SAME: %[[ARG1:.+]]: tensor<1x1x64x64xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<1x56x56x64xf32>) -> tensor<1x58x58x64xf32> { // CHECK: %[[BUFF0:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF0]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[BUFF1:.+]] = tensor.empty() : tensor<2x2x1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %[[BUFF1]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> // CHECK: %[[BUFF2:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[BUFF2]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"] @@ -397,7 +397,7 @@ func.func @conv_with_pad(%arg0: tensor<1x56x56x64xf32>, %arg1: tensor<1x1x64x64x // CHECK: %[[VAL1:.+]] = linalg.generic {indexing_maps = [#[[MAP3]]], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel"]} outs(%[[VAL]] : tensor<1x2x56x56x32xf32>) // CHECK: %[[PADDED:.+]] = tensor.pad %[[VAL1]] low[0, 0, 1, 1, 0] high[0, 0, 1, 1, 0] // CHECK: %[[OUT:.+]] = tensor.empty() : tensor<1x58x58x64xf32> -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[PADDED]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[OUT]] : tensor<1x2x58x58x32xf32> -> tensor<1x58x58x64xf32> +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[PADDED]] outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %[[OUT]] : tensor<1x2x58x58x32xf32> -> tensor<1x58x58x64xf32> // CHECK: return %[[UNPACK]] : tensor<1x58x58x64xf32> // ----- @@ -410,18 +410,18 @@ func.func @fill(%arg0: f32, %arg1: tensor<1x56x56x64xf32>, %arg2: tensor<1x1x64x %0 = tensor.empty() : tensor<1x56x56x64xf32> %1 = linalg.fill ins(%arg0 : f32) outs(%0 : tensor<1x56x56x64xf32>) -> tensor<1x56x56x64xf32> %2 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack = tensor.pack %arg1 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack = linalg.pack %arg1 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %2 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %3 = tensor.empty() : tensor<2x2x1x1x32x32xf32> - %pack_0 = tensor.pack %arg2 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %3 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> + %pack_0 = linalg.pack %arg2 outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %3 : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> %4 = tensor.empty() : tensor<1x2x56x56x32xf32> - %pack_1 = tensor.pack %1 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %4 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> + %pack_1 = linalg.pack %1 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %4 : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> %5 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%pack, %pack_0 : tensor<1x2x56x56x32xf32>, tensor<2x2x1x1x32x32xf32>) outs(%pack_1 : tensor<1x2x56x56x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %6 = arith.mulf %in, %in_2 : f32 %7 = arith.addf %out, %6 : f32 linalg.yield %7 : f32 } -> tensor<1x2x56x56x32xf32> - %unpack = tensor.unpack %5 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> + %unpack = linalg.unpack %5 outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] into %0 : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> return %unpack : tensor<1x56x56x64xf32> } @@ -434,11 +434,11 @@ func.func @fill(%arg0: f32, %arg1: tensor<1x56x56x64xf32>, %arg2: tensor<1x1x64x // CHECK-SAME: %[[ARG2:.+]]: tensor<1x1x64x64xf32> // CHECK: %[[RES:.+]] = tensor.empty() : tensor<1x56x56x64xf32> // CHECK: %[[EMPTY_ARG1:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> -// CHECK: %[[PACK_ARG1:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK_ARG1:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[EMPTY_ARG1]] : tensor<1x56x56x64xf32> -> tensor<1x2x56x56x32xf32> // CHECK: %[[EMPTY_ARG2:.+]] = tensor.empty() : tensor<2x2x1x1x32x32xf32> -// CHECK: %[[PACK_ARG2:.+]] = tensor.pack %[[ARG2]] +// CHECK: %[[PACK_ARG2:.+]] = linalg.pack %[[ARG2]] // CHECK-SAME: outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] // CHECK-SAME: into %[[EMPTY_ARG2]] : tensor<1x1x64x64xf32> -> tensor<2x2x1x1x32x32xf32> // CHECK: %[[EMPTY_FILL:.+]] = tensor.empty() : tensor<1x2x56x56x32xf32> @@ -448,7 +448,7 @@ func.func @fill(%arg0: f32, %arg1: tensor<1x56x56x64xf32>, %arg2: tensor<1x1x64x // CHECK-SAME: indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]] // CHECK-SAME: ins(%[[PACK_ARG1]], %[[PACK_ARG2]] // CHECK-SAME: outs(%[[PACKED_FILL]] -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[GEN]] +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[GEN]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[RES]] : tensor<1x2x56x56x32xf32> -> tensor<1x56x56x64xf32> @@ -467,18 +467,18 @@ func.func @matmul_with_relu_and_bias(%arg0: tensor<256x512xf32>, %arg1: tensor<5 linalg.yield %in : f32 } -> tensor<256x1024xf32> %1 = tensor.empty() : tensor<8x16x32x32xf32> - %pack = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<256x512xf32> -> tensor<8x16x32x32xf32> + %pack = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<256x512xf32> -> tensor<8x16x32x32xf32> %2 = tensor.empty() : tensor<32x16x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<512x1024xf32> -> tensor<32x16x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<512x1024xf32> -> tensor<32x16x32x32xf32> %3 = tensor.empty() : tensor<8x32x32x32xf32> - %pack_1 = tensor.pack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %3 : tensor<256x1024xf32> -> tensor<8x32x32x32xf32> + %pack_1 = linalg.pack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %3 : tensor<256x1024xf32> -> tensor<8x32x32x32xf32> %4 = linalg.generic {indexing_maps = [#map2, #map3, #map4], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %pack_0 : tensor<8x16x32x32xf32>, tensor<32x16x32x32xf32>) outs(%pack_1 : tensor<8x32x32x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %6 = arith.mulf %in, %in_2 : f32 %7 = arith.addf %out, %6 : f32 linalg.yield %7 : f32 } -> tensor<8x32x32x32xf32> - %unpack = tensor.unpack %4 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<8x32x32x32xf32> -> tensor<256x1024xf32> + %unpack = linalg.unpack %4 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<8x32x32x32xf32> -> tensor<256x1024xf32> %5 = linalg.generic {indexing_maps = [#map1], iterator_types = ["parallel", "parallel"]} outs(%unpack : tensor<256x1024xf32>) { ^bb0(%out: f32): %6 = arith.maximumf %out, %cst : f32 @@ -502,11 +502,11 @@ func.func @matmul_with_relu_and_bias(%arg0: tensor<256x512xf32>, %arg1: tensor<5 // CHECK-SAME: iterator_types = ["parallel", "parallel"] // CHECK-SAME: ins(%[[ARG3]] // CHECK-SAME: outs(%[[ARG2]] -// CHECK: %[[PACK:.+]] = tensor.pack %[[ARG0:.+]] inner_dims_pos = [0, 1] +// CHECK: %[[PACK:.+]] = linalg.pack %[[ARG0:.+]] inner_dims_pos = [0, 1] // CHECK-SAME: inner_tiles = [32, 32] into %{{.+}} : tensor<256x512xf32> -> tensor<8x16x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] // CHECK-SAME: inner_tiles = [32, 32] into %{{.+}} : tensor<512x1024xf32> -> tensor<32x16x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[BCAST]] inner_dims_pos = [0, 1] +// CHECK: %[[PACK2:.+]] = linalg.pack %[[BCAST]] inner_dims_pos = [0, 1] // CHECK-SAME: inner_tiles = [32, 32] into %{{.+}} : tensor<256x1024xf32> -> tensor<8x32x32x32xf32> // CHECK: %[[MATMUL:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP2]], #[[MAP3]], #[[MAP4]]] @@ -517,7 +517,7 @@ func.func @matmul_with_relu_and_bias(%arg0: tensor<256x512xf32>, %arg1: tensor<5 // CHECK-SAME: indexing_maps = [#[[MAP5]]] // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel"] // CHECK-SAME: outs(%[[MATMUL]] -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[RELU]] inner_dims_pos = [0, 1] +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[RELU]] inner_dims_pos = [0, 1] // CHECK-SAME: inner_tiles = [32, 32] into %[[ARG2]] : tensor<8x32x32x32xf32> -> tensor<256x1024xf32> @@ -531,7 +531,7 @@ func.func @matmul_with_relu_and_bias(%arg0: tensor<256x512xf32>, %arg1: tensor<5 // CHECK-LABEL: func.func @simple_4_layers_mlp_destination_passing // 3 packs for the first layer, 2 packs for all the others. -// CHECK-COUNT-9: tensor.pack +// CHECK-COUNT-9: linalg.pack func.func @simple_4_layers_mlp_destination_passing(%arg0: tensor<128x256xf32>, %arg1: tensor<256x512xf32>, %arg2: tensor<512xf32>, %arg3: tensor<512x1024xf32>, %arg4: tensor<1024xf32>, %arg5: tensor<1024x2048xf32>, %arg6: tensor<2048xf32>, %arg7: tensor<2048x1024xf32>, %arg8: tensor<1024xf32>, %arg9: tensor<128x1024xf32>, %arg10: tensor<128x2048xf32>, %arg11: tensor<128x1024xf32>, %arg12: tensor<128x512xf32>) -> tensor<128x1024xf32> { %cst = arith.constant 0.000000e+00 : f32 %0 = linalg.generic {indexing_maps = [#map, #map1], iterator_types = ["parallel", "parallel"]} ins(%arg2 : tensor<512xf32>) outs(%arg12 : tensor<128x512xf32>) { @@ -539,18 +539,18 @@ func.func @simple_4_layers_mlp_destination_passing(%arg0: tensor<128x256xf32>, % linalg.yield %in : f32 } -> tensor<128x512xf32> %1 = tensor.empty() : tensor<4x8x32x32xf32> - %pack = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> + %pack = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> %2 = tensor.empty() : tensor<16x8x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<256x512xf32> -> tensor<16x8x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<256x512xf32> -> tensor<16x8x32x32xf32> %3 = tensor.empty() : tensor<4x16x32x32xf32> - %pack_1 = tensor.pack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %3 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack_1 = linalg.pack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %3 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %4 = linalg.generic {indexing_maps = [#map2, #map3, #map4], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %pack_0 : tensor<4x8x32x32xf32>, tensor<16x8x32x32xf32>) outs(%pack_1 : tensor<4x16x32x32xf32>) { ^bb0(%in: f32, %in_14: f32, %out: f32): %24 = arith.mulf %in, %in_14 : f32 %25 = arith.addf %out, %24 : f32 linalg.yield %25 : f32 } -> tensor<4x16x32x32xf32> - %unpack = tensor.unpack %4 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<4x16x32x32xf32> -> tensor<128x512xf32> + %unpack = linalg.unpack %4 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<4x16x32x32xf32> -> tensor<128x512xf32> %5 = linalg.generic {indexing_maps = [#map1], iterator_types = ["parallel", "parallel"]} outs(%unpack : tensor<128x512xf32>) { ^bb0(%out: f32): %24 = arith.maximumf %out, %cst : f32 @@ -561,18 +561,18 @@ func.func @simple_4_layers_mlp_destination_passing(%arg0: tensor<128x256xf32>, % linalg.yield %in : f32 } -> tensor<128x1024xf32> %7 = tensor.empty() : tensor<4x16x32x32xf32> - %pack_2 = tensor.pack %5 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %7 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack_2 = linalg.pack %5 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %7 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %8 = tensor.empty() : tensor<32x16x32x32xf32> - %pack_3 = tensor.pack %arg3 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %8 : tensor<512x1024xf32> -> tensor<32x16x32x32xf32> + %pack_3 = linalg.pack %arg3 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %8 : tensor<512x1024xf32> -> tensor<32x16x32x32xf32> %9 = tensor.empty() : tensor<4x32x32x32xf32> - %pack_4 = tensor.pack %6 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %9 : tensor<128x1024xf32> -> tensor<4x32x32x32xf32> + %pack_4 = linalg.pack %6 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %9 : tensor<128x1024xf32> -> tensor<4x32x32x32xf32> %10 = linalg.generic {indexing_maps = [#map2, #map3, #map4], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack_2, %pack_3 : tensor<4x16x32x32xf32>, tensor<32x16x32x32xf32>) outs(%pack_4 : tensor<4x32x32x32xf32>) { ^bb0(%in: f32, %in_14: f32, %out: f32): %24 = arith.mulf %in, %in_14 : f32 %25 = arith.addf %out, %24 : f32 linalg.yield %25 : f32 } -> tensor<4x32x32x32xf32> - %unpack_5 = tensor.unpack %10 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %6 : tensor<4x32x32x32xf32> -> tensor<128x1024xf32> + %unpack_5 = linalg.unpack %10 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %6 : tensor<4x32x32x32xf32> -> tensor<128x1024xf32> %11 = linalg.generic {indexing_maps = [#map1], iterator_types = ["parallel", "parallel"]} outs(%unpack_5 : tensor<128x1024xf32>) { ^bb0(%out: f32): %24 = arith.maximumf %out, %cst : f32 @@ -583,18 +583,18 @@ func.func @simple_4_layers_mlp_destination_passing(%arg0: tensor<128x256xf32>, % linalg.yield %in : f32 } -> tensor<128x2048xf32> %13 = tensor.empty() : tensor<4x32x32x32xf32> - %pack_6 = tensor.pack %11 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %13 : tensor<128x1024xf32> -> tensor<4x32x32x32xf32> + %pack_6 = linalg.pack %11 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %13 : tensor<128x1024xf32> -> tensor<4x32x32x32xf32> %14 = tensor.empty() : tensor<64x32x32x32xf32> - %pack_7 = tensor.pack %arg5 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %14 : tensor<1024x2048xf32> -> tensor<64x32x32x32xf32> + %pack_7 = linalg.pack %arg5 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %14 : tensor<1024x2048xf32> -> tensor<64x32x32x32xf32> %15 = tensor.empty() : tensor<4x64x32x32xf32> - %pack_8 = tensor.pack %12 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %15 : tensor<128x2048xf32> -> tensor<4x64x32x32xf32> + %pack_8 = linalg.pack %12 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %15 : tensor<128x2048xf32> -> tensor<4x64x32x32xf32> %16 = linalg.generic {indexing_maps = [#map2, #map3, #map4], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack_6, %pack_7 : tensor<4x32x32x32xf32>, tensor<64x32x32x32xf32>) outs(%pack_8 : tensor<4x64x32x32xf32>) { ^bb0(%in: f32, %in_14: f32, %out: f32): %24 = arith.mulf %in, %in_14 : f32 %25 = arith.addf %out, %24 : f32 linalg.yield %25 : f32 } -> tensor<4x64x32x32xf32> - %unpack_9 = tensor.unpack %16 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %12 : tensor<4x64x32x32xf32> -> tensor<128x2048xf32> + %unpack_9 = linalg.unpack %16 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %12 : tensor<4x64x32x32xf32> -> tensor<128x2048xf32> %17 = linalg.generic {indexing_maps = [#map1], iterator_types = ["parallel", "parallel"]} outs(%unpack_9 : tensor<128x2048xf32>) { ^bb0(%out: f32): %24 = arith.maximumf %out, %cst : f32 @@ -605,24 +605,24 @@ func.func @simple_4_layers_mlp_destination_passing(%arg0: tensor<128x256xf32>, % linalg.yield %in : f32 } -> tensor<128x1024xf32> %19 = tensor.empty() : tensor<4x64x32x32xf32> - %pack_10 = tensor.pack %17 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %19 : tensor<128x2048xf32> -> tensor<4x64x32x32xf32> + %pack_10 = linalg.pack %17 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %19 : tensor<128x2048xf32> -> tensor<4x64x32x32xf32> %20 = tensor.empty() : tensor<32x64x32x32xf32> - %pack_11 = tensor.pack %arg7 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %20 : tensor<2048x1024xf32> -> tensor<32x64x32x32xf32> + %pack_11 = linalg.pack %arg7 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %20 : tensor<2048x1024xf32> -> tensor<32x64x32x32xf32> %21 = tensor.empty() : tensor<4x32x32x32xf32> - %pack_12 = tensor.pack %18 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %21 : tensor<128x1024xf32> -> tensor<4x32x32x32xf32> + %pack_12 = linalg.pack %18 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %21 : tensor<128x1024xf32> -> tensor<4x32x32x32xf32> %22 = linalg.generic {indexing_maps = [#map2, #map3, #map4], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack_10, %pack_11 : tensor<4x64x32x32xf32>, tensor<32x64x32x32xf32>) outs(%pack_12 : tensor<4x32x32x32xf32>) { ^bb0(%in: f32, %in_14: f32, %out: f32): %24 = arith.mulf %in, %in_14 : f32 %25 = arith.addf %out, %24 : f32 linalg.yield %25 : f32 } -> tensor<4x32x32x32xf32> - %unpack_13 = tensor.unpack %22 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %18 : tensor<4x32x32x32xf32> -> tensor<128x1024xf32> + %unpack_13 = linalg.unpack %22 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %18 : tensor<4x32x32x32xf32> -> tensor<128x1024xf32> %23 = linalg.generic {indexing_maps = [#map1], iterator_types = ["parallel", "parallel"]} outs(%unpack_13 : tensor<128x1024xf32>) { ^bb0(%out: f32): %24 = arith.maximumf %out, %cst : f32 linalg.yield %24 : f32 } -> tensor<128x1024xf32> - // CHECK: %[[UNPACK:.+]] = tensor.unpack %{{.+}} inner_dims_pos = [0, 1] + // CHECK: %[[UNPACK:.+]] = linalg.unpack %{{.+}} inner_dims_pos = [0, 1] // CHECK-SAME: inner_tiles = [32, 32] into %{{.+}} : tensor<4x32x32x32xf32> -> tensor<128x1024xf32> // CHECK-NEXT: return %[[UNPACK]] : tensor<128x1024xf32> return %23 : tensor<128x1024xf32> diff --git a/test/Passes/pack-vnni.mlir b/test/Passes/pack-vnni.mlir index e30050712..79809bad8 100644 --- a/test/Passes/pack-vnni.mlir +++ b/test/Passes/pack-vnni.mlir @@ -21,7 +21,7 @@ module attributes { // CHECK-SAME: %[[ARG2:.+]]: tensor<32x32xbf16> // CHECK: %[[VNNI_A:.+]] = tensor.expand_shape %[[ARG0]] {{\[}}[0], [1], [2, 3]] // CHECK-SAME: output_shape{{.*}}: tensor<5x32x64xbf16> into tensor<5x32x32x2xbf16> -// CHECK: %[[PACK:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: inner_dims_pos = [1] inner_tiles = [2] // CHECK-SAME: : tensor<5x64x32xbf16> -> tensor<5x32x32x2xbf16> // CHECK: linalg.generic @@ -53,7 +53,7 @@ module attributes { // CHECK-SAME: %[[ARG2:.+]]: tensor<32x32xbf16> // CHECK: %[[VNNI_A:.+]] = tensor.expand_shape %[[ARG0]] {{\[}}[0], [1], [2, 3]] // CHECK-SAME: output_shape{{.*}}: tensor<5x32x64xbf16> into tensor<5x32x16x4xbf16> -// CHECK: %[[PACK:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: inner_dims_pos = [1] inner_tiles = [4] // CHECK-SAME: : tensor<5x64x32xbf16> -> tensor<5x16x32x4xbf16> // CHECK: linalg.generic diff --git a/test/Passes/pass-conv-blocking-nchw-fchw-default.mlir b/test/Passes/pass-conv-blocking-nchw-fchw-default.mlir index 67de5b9c5..33b855812 100644 --- a/test/Passes/pass-conv-blocking-nchw-fchw-default.mlir +++ b/test/Passes/pass-conv-blocking-nchw-fchw-default.mlir @@ -15,11 +15,11 @@ func.func @conv_2d_nchw_fchw(%i: tensor<14x512x28x28xf32>, %f: tensor<1024x512x1 // CHECK-SAME: %[[ARG1:.+]]: tensor<1024x512x1x1xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<14x1024x28x28xf32>) -> tensor<14x1024x28x28xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<14x16x28x28x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF0]] : tensor<14x512x28x28xf32> -> tensor<14x16x28x28x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF0]] : tensor<14x512x28x28xf32> -> tensor<14x16x28x28x32xf32> // CHECK: %[[BUF1:.+]] = tensor.empty() : tensor<32x16x1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF1]] : tensor<1024x512x1x1xf32> -> tensor<32x16x1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF1]] : tensor<1024x512x1x1xf32> -> tensor<32x16x1x1x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<14x32x28x28x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF2]] : tensor<14x1024x28x28xf32> -> tensor<14x32x28x28x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF2]] : tensor<14x1024x28x28xf32> -> tensor<14x32x28x28x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<14x16x28x28x32xf32>, tensor<32x16x1x1x32x32xf32>) outs(%[[PACK2]] : tensor<14x32x28x28x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [1] inner_tiles = [32] into %[[ARG2]] : tensor<14x32x28x28x32xf32> -> tensor<14x1024x28x28xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [1] inner_tiles = [32] into %[[ARG2]] : tensor<14x32x28x28x32xf32> -> tensor<14x1024x28x28xf32> // CHECK: return %[[OUT]] : tensor<14x1024x28x28xf32> diff --git a/test/Passes/pass-conv-blocking-nchw-fchw.mlir b/test/Passes/pass-conv-blocking-nchw-fchw.mlir index 76ea6a0fa..122307ba4 100644 --- a/test/Passes/pass-conv-blocking-nchw-fchw.mlir +++ b/test/Passes/pass-conv-blocking-nchw-fchw.mlir @@ -15,13 +15,13 @@ func.func @conv_2d_nchw_fchw(%i: tensor<14x512x28x28xf32>, %f: tensor<1024x512x1 // CHECK-SAME: %[[ARG1:.+]]: tensor<1024x512x1x1xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<14x1024x28x28xf32>) -> tensor<14x1024x28x28xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<14x16x28x28x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF0]] : tensor<14x512x28x28xf32> -> tensor<14x16x28x28x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF0]] : tensor<14x512x28x28xf32> -> tensor<14x16x28x28x32xf32> // CHECK: %[[BUF1:.+]] = tensor.empty() : tensor<32x16x1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF1]] : tensor<1024x512x1x1xf32> -> tensor<32x16x1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF1]] : tensor<1024x512x1x1xf32> -> tensor<32x16x1x1x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<14x32x28x28x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF2]] : tensor<14x1024x28x28xf32> -> tensor<14x32x28x28x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [1] inner_tiles = [32] into %[[BUF2]] : tensor<14x1024x28x28xf32> -> tensor<14x32x28x28x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP0]], #[[MAP1]], #[[MAP2]]], iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<14x16x28x28x32xf32>, tensor<32x16x1x1x32x32xf32>) outs(%[[PACK2]] : tensor<14x32x28x28x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [1] inner_tiles = [32] into %[[ARG2]] : tensor<14x32x28x28x32xf32> -> tensor<14x1024x28x28xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [1] inner_tiles = [32] into %[[ARG2]] : tensor<14x32x28x28x32xf32> -> tensor<14x1024x28x28xf32> // CHECK: return %[[OUT]] : tensor<14x1024x28x28xf32> // CHECK: } diff --git a/test/Passes/pass-conv-blocking-nhwc-hwcf-default.mlir b/test/Passes/pass-conv-blocking-nhwc-hwcf-default.mlir index 3582c5db9..8cbb0519a 100644 --- a/test/Passes/pass-conv-blocking-nhwc-hwcf-default.mlir +++ b/test/Passes/pass-conv-blocking-nhwc-hwcf-default.mlir @@ -17,15 +17,15 @@ func.func @conv_2d_nhwc_hwcf(%arg0: tensor<1x113x113x64xf32>, %arg1: tensor<3x3x // CHECK-SAME: %[[ARG1:.+]]: tensor<3x3x64x256xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<1x111x111x256xf32>) // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<1x2x113x113x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[BUF0]] : tensor<1x113x113x64xf32> -> tensor<1x2x113x113x32xf32> // CHECK: %[[BUF1:.+]] = tensor.empty() : tensor<8x2x3x3x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] // CHECK-SAME: into %[[BUF1]] : tensor<3x3x64x256xf32> -> tensor<8x2x3x3x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<1x8x111x111x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[BUF2]] : tensor<1x111x111x256xf32> -> tensor<1x8x111x111x32xf32> // CHECK: %[[GEN:.+]] = linalg.generic { @@ -33,7 +33,7 @@ func.func @conv_2d_nhwc_hwcf(%arg0: tensor<1x113x113x64xf32>, %arg1: tensor<3x3x // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} // CHECK-SAME: ins(%[[PACK0]], %[[PACK1]] // CHECK-SAME: outs(%[[PACK2]] -// CHECK: %[[RES:.+]] = tensor.unpack %[[GEN]] +// CHECK: %[[RES:.+]] = linalg.unpack %[[GEN]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[ARG2]] : tensor<1x8x111x111x32xf32> -> tensor<1x111x111x256xf32> // CHECK: return %[[RES]] : tensor<1x111x111x256xf32> diff --git a/test/Passes/pass-conv-blocking-nhwc-hwcf.mlir b/test/Passes/pass-conv-blocking-nhwc-hwcf.mlir index 8ace2eba8..066248679 100644 --- a/test/Passes/pass-conv-blocking-nhwc-hwcf.mlir +++ b/test/Passes/pass-conv-blocking-nhwc-hwcf.mlir @@ -17,15 +17,15 @@ func.func @conv_2d_nhwc_hwcf(%arg0: tensor<1x113x113x64xf32>, %arg1: tensor<3x3x // CHECK-SAME: %[[ARG1:.+]]: tensor<3x3x64x256xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<1x111x111x256xf32>) // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<1x2x113x113x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[BUF0]] : tensor<1x113x113x64xf32> -> tensor<1x2x113x113x32xf32> // CHECK: %[[BUF1:.+]] = tensor.empty() : tensor<8x2x3x3x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] // CHECK-SAME: into %[[BUF1]] : tensor<3x3x64x256xf32> -> tensor<8x2x3x3x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<1x8x111x111x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[BUF2]] : tensor<1x111x111x256xf32> -> tensor<1x8x111x111x32xf32> // CHECK: %[[GEN:.+]] = linalg.generic { @@ -33,7 +33,7 @@ func.func @conv_2d_nhwc_hwcf(%arg0: tensor<1x113x113x64xf32>, %arg1: tensor<3x3x // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} // CHECK-SAME: ins(%[[PACK0]], %[[PACK1]] // CHECK-SAME: outs(%[[PACK2]] -// CHECK: %[[RES:.+]] = tensor.unpack %[[GEN]] +// CHECK: %[[RES:.+]] = linalg.unpack %[[GEN]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[ARG2]] : tensor<1x8x111x111x32xf32> -> tensor<1x111x111x256xf32> // CHECK: return %[[RES]] : tensor<1x111x111x256xf32> @@ -58,15 +58,15 @@ func.func @main(%arg0: tensor<1x113x113x64xf32>, %arg1: tensor<3x3x64x256xf32>, // CHECK-SAME: %[[ARG1:.+]]: tensor<3x3x64x256xf32>, // CHECK-SAME: %[[ARG2:.+]]: tensor<1x56x56x256xf32> // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<1x2x113x113x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[BUF0]] : tensor<1x113x113x64xf32> -> tensor<1x2x113x113x32xf32> // CHECK: %[[BUF1:.+]] = tensor.empty() : tensor<8x2x3x3x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] // CHECK-SAME: into %[[BUF1]] : tensor<3x3x64x256xf32> -> tensor<8x2x3x3x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<1x8x56x56x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[BUF2]] : tensor<1x56x56x256xf32> -> tensor<1x8x56x56x32xf32> // CHECK: %[[GEN:.+]] = linalg.generic { @@ -74,7 +74,7 @@ func.func @main(%arg0: tensor<1x113x113x64xf32>, %arg1: tensor<3x3x64x256xf32>, // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "parallel", "parallel", "reduction", "reduction", "reduction", "reduction"]} // CHECK-SAME: ins(%[[PACK0]], %[[PACK1]] // CHECK-SAME: outs(%[[PACK2]] : tensor<1x8x56x56x32xf32>) -// CHECK: %[[RES:.+]] = tensor.unpack %[[GEN]] +// CHECK: %[[RES:.+]] = linalg.unpack %[[GEN]] // CHECK-SAME: outer_dims_perm = [0, 3, 1, 2] inner_dims_pos = [3] inner_tiles = [32] // CHECK-SAME: into %[[ARG2]] : tensor<1x8x56x56x32xf32> -> tensor<1x56x56x256xf32> // CHECK: return %[[RES]] : tensor<1x56x56x256xf32> diff --git a/test/Passes/pass-matmul-blocking-default.mlir b/test/Passes/pass-matmul-blocking-default.mlir index 8e00f1306..425bc7d0f 100644 --- a/test/Passes/pass-matmul-blocking-default.mlir +++ b/test/Passes/pass-matmul-blocking-default.mlir @@ -18,13 +18,13 @@ func.func @block_linalg_matmul( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<128x128xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<128x128xf32>) -> tensor<128x128xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF1:.*]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<4x4x32x32xf32>, tensor<4x4x32x32xf32>) outs(%[[PACK2]] : tensor<4x4x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> // CHECK: return %[[OUT]] : tensor<128x128xf32> // ----- @@ -47,13 +47,13 @@ func.func @block_linalg_matmul_transpose_a( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<128x128xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<128x128xf32>) -> tensor<128x128xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [1, 0] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [1, 0] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF1:.*]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<4x4x32x32xf32>, tensor<4x4x32x32xf32>) outs(%[[PACK2]] : tensor<4x4x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> // CHECK: return %[[OUT]] : tensor<128x128xf32> // ----- @@ -76,13 +76,13 @@ func.func @block_linalg_matmul_transpose_b( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<128x128xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<128x128xf32>) -> tensor<128x128xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF1:.*]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [0, 1] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [0, 1] inner_dims_pos = [1, 0] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<4x4x32x32xf32>, tensor<4x4x32x32xf32>) outs(%[[PACK2]] : tensor<4x4x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> // CHECK: return %[[OUT]] : tensor<128x128xf32> // ----- @@ -105,18 +105,18 @@ func.func @block_linalg_matmul_dynamic( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor) -> tensor { // CHECK-DAG: %[[PAD:.+]] = arith.constant 0.000000e+00 : f32 -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] padding_value(%[[PAD]] : f32) +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] padding_value(%[[PAD]] : f32) // CHECK-SAME: outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] // CHECK-SAME: inner_tiles = [32, 32] into {{.*}} : tensor -> tensor -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] padding_value(%[[PAD]] : f32) +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] padding_value(%[[PAD]] : f32) // CHECK-SAME: outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] // CHECK-SAME: inner_tiles = [32, 32] into {{.*}} : tensor -> tensor -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] padding_value(%[[PAD]] : f32) +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] padding_value(%[[PAD]] : f32) // CHECK-SAME: inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into {{.*}} : tensor -> tensor // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], // CHECK-SAME: iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} // CHECK-SAME: ins(%[[PACK0]], %[[PACK1]] : tensor, tensor) outs(%[[PACK2]] : tensor) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[ARG2]] : tensor -> tensor // CHECK: return %[[OUT]] : tensor diff --git a/test/Passes/pass-matmul-blocking.mlir b/test/Passes/pass-matmul-blocking.mlir index 830d38916..e3ce5c605 100644 --- a/test/Passes/pass-matmul-blocking.mlir +++ b/test/Passes/pass-matmul-blocking.mlir @@ -18,13 +18,13 @@ func.func @block_linalg_matmul( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<128x128xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<128x128xf32>) -> tensor<128x128xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF1:.*]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<4x4x32x32xf32>, tensor<4x4x32x32xf32>) outs(%[[PACK2]] : tensor<4x4x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> // CHECK: return %[[OUT]] : tensor<128x128xf32> // CHECK: } @@ -48,13 +48,13 @@ func.func @block_dims_equal_to_factors( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<32x32xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<32x32xf32>) -> tensor<32x32xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<1x1x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<32x32xf32> -> tensor<1x1x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<32x32xf32> -> tensor<1x1x32x32xf32> // CHECK: %[[BUF1:.*]] = tensor.empty() : tensor<1x1x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<32x32xf32> -> tensor<1x1x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<32x32xf32> -> tensor<1x1x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<1x1x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<32x32xf32> -> tensor<1x1x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<32x32xf32> -> tensor<1x1x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<1x1x32x32xf32>, tensor<1x1x32x32xf32>) outs(%[[PACK2]] : tensor<1x1x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<1x1x32x32xf32> -> tensor<32x32xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<1x1x32x32xf32> -> tensor<32x32xf32> // CHECK: return %[[OUT]] : tensor<32x32xf32> // CHECK: } @@ -81,13 +81,13 @@ func.func @block_small_dims_matmul( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<6x5xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<5x5xf32>) -> tensor<5x5xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<1x1x5x6xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [5, 6] into %[[BUF0]] : tensor<5x6xf32> -> tensor<1x1x5x6xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [5, 6] into %[[BUF0]] : tensor<5x6xf32> -> tensor<1x1x5x6xf32> // CHECK: %[[BUF1:.*]] = tensor.empty() : tensor<1x1x6x5xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [6, 5] into %[[BUF1]] : tensor<6x5xf32> -> tensor<1x1x6x5xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [6, 5] into %[[BUF1]] : tensor<6x5xf32> -> tensor<1x1x6x5xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<1x1x5x5xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [5, 5] into %[[BUF2]] : tensor<5x5xf32> -> tensor<1x1x5x5xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [5, 5] into %[[BUF2]] : tensor<5x5xf32> -> tensor<1x1x5x5xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<1x1x5x6xf32>, tensor<1x1x6x5xf32>) outs(%[[PACK2]] : tensor<1x1x5x5xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [5, 5] into %[[ARG2]] : tensor<1x1x5x5xf32> -> tensor<5x5xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [5, 5] into %[[ARG2]] : tensor<1x1x5x5xf32> -> tensor<5x5xf32> // CHECK: return %[[OUT]] : tensor<5x5xf32> // CHECK: } @@ -111,18 +111,18 @@ func.func @block_linalg_matmul( // CHECK: %[[FILL:.+]] = linalg.fill ins(%[[CST]] : f32) // CHECK-SAME: outs(%[[ARG2]] : tensor<128x128xf32>) -> tensor<128x128xf32> // CHECK: %[[EMPTY_ARG0:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK_ARG0:.+]] = tensor.pack %[[ARG0]] +// CHECK: %[[PACK_ARG0:.+]] = linalg.pack %[[ARG0]] // CHECK-SAME: outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[EMPTY_ARG0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[EMPTY_ARG1:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK_ARG1:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK_ARG1:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[EMPTY_ARG1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[EMPTY_FILL:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK_FILL:.+]] = tensor.pack %[[FILL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] +// CHECK: %[[PACK_FILL:.+]] = linalg.pack %[[FILL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[EMPTY_FILL]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[RES:.+]] = linalg.generic -// CHECK: %{{.+}} = tensor.unpack %[[RES]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] +// CHECK: %{{.+}} = linalg.unpack %[[RES]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[FILL]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> // ----- @@ -154,13 +154,13 @@ func.func @block_linalg_matmul( // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<128x128xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<128x128xf32>) -> tensor<128x128xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] outer_dims_perm = [0, 1] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF1:.*]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[VAL:.+]] = linalg.generic {indexing_maps = [#[[MAP3]], #[[MAP4]], #[[MAP5]]], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%[[PACK0]], %[[PACK1]] : tensor<4x4x32x32xf32>, tensor<4x4x32x32xf32>) outs(%[[PACK2]] : tensor<4x4x32x32xf32>) -// CHECK: %[[OUT:.+]] = tensor.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> +// CHECK: %[[OUT:.+]] = linalg.unpack %[[VAL]] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %[[ARG2]] : tensor<4x4x32x32xf32> -> tensor<128x128xf32> // CHECK: return %[[OUT]] : tensor<128x128xf32> // ----- @@ -179,20 +179,20 @@ func.func @batch_matmul_rewrite(%arg0: tensor<512x64x128xf32>, %arg1: tensor<512 // CHECK-SAME: %[[ARG0:.+]]: tensor<512x64x128xf32>, %[[ARG1:.+]]: tensor<512x128x64xf32> // CHECK: %[[OUT:.+]] = tensor.empty() : tensor<512x64x64xf32> // CHECK: %[[ARG0_PACK_OUT:.+]] = tensor.empty() : tensor<512x2x4x32x32xf32> -// CHECK: %[[ARG0_PACK:.+]] = tensor.pack %[[ARG0]] +// CHECK: %[[ARG0_PACK:.+]] = linalg.pack %[[ARG0]] // CHECK-SAME: outer_dims_perm = [0, 1, 2] inner_dims_pos = [1, 2] inner_tiles = [32, 32] // CHECK-SAME: into %[[ARG0_PACK_OUT]] : tensor<512x64x128xf32> -> tensor<512x2x4x32x32xf32> // CHECK: %[[ARG1_PACK_OUT:.+]] = tensor.empty() : tensor<512x2x4x32x32xf32> -// CHECK: %[[ARG1_PACK:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[ARG1_PACK:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: outer_dims_perm = [0, 2, 1] inner_dims_pos = [1, 2] inner_tiles = [32, 32] // CHECK-SAME: into %[[ARG1_PACK_OUT]] : tensor<512x128x64xf32> -> tensor<512x2x4x32x32xf32> // CHECK: %[[OUT_PACK_OUT:.+]] = tensor.empty() : tensor<512x2x2x32x32xf32> -// CHECK: %[[OUT_PACK:.+]] = tensor.pack %[[OUT]] +// CHECK: %[[OUT_PACK:.+]] = linalg.pack %[[OUT]] // CHECK-SAME: inner_dims_pos = [1, 2] inner_tiles = [32, 32] // CHECK-SAME: into %[[OUT_PACK_OUT]] : tensor<512x64x64xf32> -> tensor<512x2x2x32x32xf32> // CHECK: %[[GEN:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#[[MAP]], #[[MAP1]], #[[MAP2]]] // CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel", "reduction", "parallel", "parallel", "reduction"] -// CHECK: %[[UNPACK:.+]] = tensor.unpack %[[GEN]] +// CHECK: %[[UNPACK:.+]] = linalg.unpack %[[GEN]] // CHECK-SAME: inner_dims_pos = [1, 2] inner_tiles = [32, 32] // CHECK-SAME: into %[[OUT]] : tensor<512x2x2x32x32xf32> -> tensor<512x64x64xf32> diff --git a/test/Passes/pass-matmul-fuse.mlir b/test/Passes/pass-matmul-fuse.mlir index df0d97204..41ca1b49b 100644 --- a/test/Passes/pass-matmul-fuse.mlir +++ b/test/Passes/pass-matmul-fuse.mlir @@ -21,15 +21,15 @@ func.func @matmul_and_relu(%arg0: tensor<128x128xf32>, %arg1: tensor<128x128xf32 // CHECK-SAME: %[[ARG1:[0-9a-z]+]]: tensor<128x128xf32> // CHECK-SAME: %[[ARG2:[0-9a-z]+]]: tensor<128x128xf32>) -> tensor<128x128xf32> { // CHECK: %[[BUF0:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK0:.+]] = tensor.pack %[[ARG0]] +// CHECK: %[[PACK0:.+]] = linalg.pack %[[ARG0]] // CHECK-SAME: inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[BUF0]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF1:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK1:.+]] = tensor.pack %[[ARG1]] +// CHECK: %[[PACK1:.+]] = linalg.pack %[[ARG1]] // CHECK-SAME: outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[BUF1]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[BUF2:.+]] = tensor.empty() : tensor<4x4x32x32xf32> -// CHECK: %[[PACK2:.+]] = tensor.pack %[[ARG2]] +// CHECK: %[[PACK2:.+]] = linalg.pack %[[ARG2]] // CHECK-SAME: inner_dims_pos = [0, 1] inner_tiles = [32, 32] // CHECK-SAME: into %[[BUF2]] : tensor<128x128xf32> -> tensor<4x4x32x32xf32> // CHECK: %[[LOOP:.+]] = scf.forall (%[[ARG3:.+]], %[[ARG4:.+]]) in (4, 4) diff --git a/test/Passes/pass-pack-lowering.mlir b/test/Passes/pass-pack-lowering.mlir index 30c3e0725..fc449150c 100644 --- a/test/Passes/pass-pack-lowering.mlir +++ b/test/Passes/pass-pack-lowering.mlir @@ -1,7 +1,7 @@ // RUN: tpp-opt %s -lower-packs-unpacks -split-input-file | FileCheck %s func.func @matmul_pack(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x32x32xbf16>) -> tensor<16x32x32x32xbf16> { - %pack = tensor.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<1024x512xbf16> -> tensor<16x32x32x32xbf16> + %pack = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<1024x512xbf16> -> tensor<16x32x32x32xbf16> return %pack : tensor<16x32x32x32xbf16> } @@ -21,7 +21,7 @@ func.func @matmul_pack(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x32x32xb // ----- func.func @matmul_unpack(%arg0: tensor<16x16x32x32xbf16>, %arg1: tensor<512x512xbf16>) -> tensor<512x512xbf16> { - %unpack = tensor.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<16x16x32x32xbf16> -> tensor<512x512xbf16> + %unpack = linalg.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<16x16x32x32xbf16> -> tensor<512x512xbf16> return %unpack : tensor<512x512xbf16> } @@ -42,9 +42,9 @@ func.func @matmul_unpack(%arg0: tensor<16x16x32x32xbf16>, %arg1: tensor<512x512x func.func @pack_fusion(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x16x32x2xbf16>) -> tensor<16x32x16x32x2xbf16> { %1 = tensor.empty() : tensor<16x32x32x32xbf16> - %pack_0 = tensor.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 + %pack_0 = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<1024x512xbf16> -> tensor<16x32x32x32xbf16> - %pack_1 = tensor.pack %pack_0 inner_dims_pos = [2] inner_tiles = [2] into %arg1 + %pack_1 = linalg.pack %pack_0 inner_dims_pos = [2] inner_tiles = [2] into %arg1 : tensor<16x32x32x32xbf16> -> tensor<16x32x16x32x2xbf16> return %pack_1 : tensor<16x32x16x32x2xbf16> } @@ -71,11 +71,11 @@ func.func @pack_fusion(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x16x32x2 func.func @expect_to_fuse_first_and_second(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x16x32x2xbf16>, %arg2: tensor<8x32x16x32x2x2xbf16>) -> tensor<8x32x16x32x2x2xbf16> { %1 = tensor.empty() : tensor<16x32x32x32xbf16> - %pack_0 = tensor.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 + %pack_0 = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<1024x512xbf16> -> tensor<16x32x32x32xbf16> - %pack_1 = tensor.pack %pack_0 inner_dims_pos = [2] inner_tiles = [2] into %arg1 + %pack_1 = linalg.pack %pack_0 inner_dims_pos = [2] inner_tiles = [2] into %arg1 : tensor<16x32x32x32xbf16> -> tensor<16x32x16x32x2xbf16> - %pack_2 = tensor.pack %pack_1 inner_dims_pos = [0] inner_tiles = [2] into %arg2 + %pack_2 = linalg.pack %pack_1 inner_dims_pos = [0] inner_tiles = [2] into %arg2 : tensor<16x32x16x32x2xbf16> -> tensor<8x32x16x32x2x2xbf16> return %pack_2 : tensor<8x32x16x32x2x2xbf16> } @@ -102,9 +102,9 @@ func.func @expect_to_fuse_first_and_second(%arg0: tensor<1024x512xbf16>, %arg1: func.func @expect_not_to_fuse(%arg0: tensor<1024x512xbf16>, %arg1: tensor<8x32x32x32x2xbf16>) -> tensor<8x32x32x32x2xbf16> { %1 = tensor.empty() : tensor<16x32x32x32xbf16> - %pack_0 = tensor.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 + %pack_0 = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<1024x512xbf16> -> tensor<16x32x32x32xbf16> - %pack_1 = tensor.pack %pack_0 inner_dims_pos = [0] inner_tiles = [2] into %arg1 + %pack_1 = linalg.pack %pack_0 inner_dims_pos = [0] inner_tiles = [2] into %arg1 : tensor<16x32x32x32xbf16> -> tensor<8x32x32x32x2xbf16> return %pack_1 : tensor<8x32x32x32x2xbf16> } @@ -128,9 +128,9 @@ func.func @expect_not_to_fuse(%arg0: tensor<1024x512xbf16>, %arg1: tensor<8x32x3 func.func @pack_fusion_outer_only(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x16x32x32x2xbf16>) -> tensor<16x16x32x32x2xbf16> { %1 = tensor.empty() : tensor<16x32x32x32xbf16> - %pack_0 = tensor.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 + %pack_0 = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<1024x512xbf16> -> tensor<16x32x32x32xbf16> - %pack_1 = tensor.pack %pack_0 inner_dims_pos = [1] inner_tiles = [2] into %arg1 + %pack_1 = linalg.pack %pack_0 inner_dims_pos = [1] inner_tiles = [2] into %arg1 : tensor<16x32x32x32xbf16> -> tensor<16x16x32x32x2xbf16> return %pack_1 : tensor<16x16x32x32x2xbf16> } @@ -157,7 +157,7 @@ func.func @pack_fusion_outer_only(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16 // ----- func.func @vnni_packing(%arg0: tensor<16x16xbf16>, %arg1: tensor<8x16x2xbf16>) -> tensor<8x16x2xbf16> { - %pack = tensor.pack %arg0 inner_dims_pos = [0] inner_tiles = [2] into %arg1 : tensor<16x16xbf16> -> tensor<8x16x2xbf16> + %pack = linalg.pack %arg0 inner_dims_pos = [0] inner_tiles = [2] into %arg1 : tensor<16x16xbf16> -> tensor<8x16x2xbf16> return %pack : tensor<8x16x2xbf16> } diff --git a/test/Passes/simplify-pack.mlir b/test/Passes/simplify-pack.mlir index 8e8e1d321..df11a60ab 100644 --- a/test/Passes/simplify-pack.mlir +++ b/test/Passes/simplify-pack.mlir @@ -2,12 +2,12 @@ // CHECK-LABEL: empty_static func.func @empty_static() -> tensor<64x16x32x32xf32> { - // CHECK-NOT: tensor.pack + // CHECK-NOT: linalg.pack // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<64x16x32x32xf32> // CHECK-NEXT: return %[[EMPTY]] : tensor<64x16x32x32xf32> %0 = tensor.empty() : tensor<2048x512xf32> %1 = tensor.empty() : tensor<64x16x32x32xf32> - %pack = tensor.pack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<2048x512xf32> -> tensor<64x16x32x32xf32> + %pack = linalg.pack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<2048x512xf32> -> tensor<64x16x32x32xf32> return %pack : tensor<64x16x32x32xf32> } @@ -15,12 +15,12 @@ func.func @empty_static() -> tensor<64x16x32x32xf32> { // CHECK-LABEL: empty_partially_dynamic func.func @empty_partially_dynamic(%tile1: index, %tile2: index) -> tensor<16x16x?x?xf32> { - // CHECK-NOT: tensor.pack + // CHECK-NOT: linalg.pack // CHECK: %[[EMPTY:.+]] = tensor.empty(%{{.+}}, %{{.+}}) : tensor<16x16x?x?xf32> // CHECK-NEXT: return %[[EMPTY]] : tensor<16x16x?x?xf32> %0 = tensor.empty() : tensor<128x128xf32> %1 = tensor.empty(%tile1, %tile2) : tensor<16x16x?x?xf32> - %pack = tensor.pack %0 inner_dims_pos = [0, 1] inner_tiles = [%tile1, %tile2] into %1 : tensor<128x128xf32> -> tensor<16x16x?x?xf32> + %pack = linalg.pack %0 inner_dims_pos = [0, 1] inner_tiles = [%tile1, %tile2] into %1 : tensor<128x128xf32> -> tensor<16x16x?x?xf32> return %pack : tensor<16x16x?x?xf32> } @@ -29,12 +29,12 @@ func.func @empty_partially_dynamic(%tile1: index, %tile2: index) -> tensor<16x16 // CHECK-LABEL: empty_fully_dynamic func.func @empty_fully_dynamic(%tile1: index, %tile2: index, %tile3: index, %tile4: index, %i: index, %j: index) -> tensor { - // CHECK-NOT: tensor.pack + // CHECK-NOT: linalg.pack // CHECK: %[[EMPTY:.+]] = tensor.empty(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) : tensor // CHECK-NEXT: return %[[EMPTY]] : tensor %0 = tensor.empty(%i, %j) : tensor %1 = tensor.empty(%tile1, %tile2, %tile3, %tile4) : tensor - %pack = tensor.pack %0 inner_dims_pos = [0, 1] inner_tiles = [%tile1, %tile2] into %1 : tensor -> tensor + %pack = linalg.pack %0 inner_dims_pos = [0, 1] inner_tiles = [%tile1, %tile2] into %1 : tensor -> tensor return %pack : tensor } @@ -43,8 +43,8 @@ func.func @empty_fully_dynamic(%tile1: index, %tile2: index, %tile3: index, %til // CHECK-LABEL: noop_pack // CHECK-SAME: %[[ARG0:.+]]: tensor<32x32xbf16>, %[[ARG1:.+]]: tensor<1x1x32x32xbf16> func.func @noop_pack(%arg0: tensor<32x32xbf16>, %arg1: tensor<1x1x32x32xbf16>) -> tensor<1x1x32x32xbf16> { - // CHECK-NOT: tensor.pack - %0 = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 + // CHECK-NOT: linalg.pack + %0 = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<32x32xbf16> -> tensor<1x1x32x32xbf16> // CHECK: %[[EXP:.+]] = tensor.expand_shape %[[ARG0]] {{\[}}[0, 1, 2], [3]] // CHECK-SAME: : tensor<32x32xbf16> into tensor<1x1x32x32xbf16> @@ -57,8 +57,8 @@ func.func @noop_pack(%arg0: tensor<32x32xbf16>, %arg1: tensor<1x1x32x32xbf16>) - // CHECK-LABEL: noop_pack_1 // CHECK-SAME: %[[ARG0:.+]]: tensor<32x32xbf16>, %[[ARG1:.+]]: tensor<1x1x32x32xbf16> func.func @noop_pack_1(%arg0: tensor<32x32xbf16>, %arg1: tensor<1x1x32x32xbf16>) -> tensor<1x1x32x32xbf16> { - // CHECK-NOT: tensor.pack - %0 = tensor.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 + // CHECK-NOT: linalg.pack + %0 = linalg.pack %arg0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<32x32xbf16> -> tensor<1x1x32x32xbf16> // CHECK: %[[EXP:.+]] = tensor.expand_shape %[[ARG0]] {{\[}}[0, 1, 2], [3]] // CHECK-SAME: : tensor<32x32xbf16> into tensor<1x1x32x32xbf16> @@ -71,8 +71,8 @@ func.func @noop_pack_1(%arg0: tensor<32x32xbf16>, %arg1: tensor<1x1x32x32xbf16>) // CHECK-LABEL: op_pack_2 func.func @op_pack_2(%arg0: tensor<30x30xbf16>, %arg1: tensor<1x1x32x32xbf16>) -> tensor<1x1x32x32xbf16> { %pad = arith.constant 0.0 : bf16 - // CHECK: tensor.pack - %0 = tensor.pack %arg0 padding_value(%pad : bf16) outer_dims_perm = [1, 0] + // CHECK: linalg.pack + %0 = linalg.pack %arg0 padding_value(%pad : bf16) outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<30x30xbf16> -> tensor<1x1x32x32xbf16> // CHECK-NOT: tensor.expand_shape @@ -86,8 +86,8 @@ func.func @op_pack_3(%arg0: tensor<32x64xbf16>, %arg1: tensor<1x2x32x32xbf16>) - // We cannot simplify the pack, dropping dimension 0 would mean the following pack: // %arg0 inner_dims_pos = [1] inner_tiles = [32] -> 32x2x32xbf16 // which is different from 2x32x32xbf16 - // CHECK: tensor.pack - %0 = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 + // CHECK: linalg.pack + %0 = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor<32x64xbf16> -> tensor<1x2x32x32xbf16> // CHECK-NOT: tensor.expand_shape return %0 : tensor<1x2x32x32xbf16> @@ -100,8 +100,8 @@ func.func @op_pack_4(%arg0: tensor<32x64x64xbf16>, %arg1: tensor<1x2x2x32x32x32x // We cannot simplify the pack, dropping dimension 0, would mean the following pack: // %arg0 inner_dims_pos = [1, 2] inner_tiles = [32, 32] -> 32x2x2x32x32xbf16 // which is different from 2x2x32x32x32xbf16. - // CHECK: tensor.pack - %0 = tensor.pack %arg0 inner_dims_pos = [0, 1, 2] inner_tiles = [32, 32, 32] into %arg1 + // CHECK: linalg.pack + %0 = linalg.pack %arg0 inner_dims_pos = [0, 1, 2] inner_tiles = [32, 32, 32] into %arg1 : tensor<32x64x64xbf16> -> tensor<1x2x2x32x32x32xbf16> // CHECK-NOT: tensor.expand_shape return %0 : tensor<1x2x2x32x32x32xbf16> @@ -113,9 +113,9 @@ func.func @op_pack_4(%arg0: tensor<32x64x64xbf16>, %arg1: tensor<1x2x2x32x32x32x // CHECK-SAME: %[[ARG0:.+]]: tensor, %[[ARG1:.+]]: tensor<1x1x32x32xbf16> // This should reshape. What about dynamic tiles? func.func @op_pack_5(%arg0: tensor, %arg1: tensor<1x1x32x32xbf16>) -> tensor<1x1x32x32xbf16> { - // CHECK: tensor.pack + // CHECK: linalg.pack // We bail out as we have unknown dim. - %0 = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 + %0 = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1 : tensor -> tensor<1x1x32x32xbf16> // CHECK-NOT: tensor.expand_shape return %0 : tensor<1x1x32x32xbf16> @@ -127,12 +127,12 @@ func.func @op_pack_5(%arg0: tensor, %arg1: tensor<1x1x32x32xbf16>) -> // CHECK-SAME: %[[ARG0:.+]]: tensor<32x32xbf16>, %[[ARG1:.+]]: tensor<1x16x32x2xbf16> func.func @rank_reduce_pack(%arg0: tensor<32x32xbf16>, %arg1: tensor<1x16x32x2xbf16>) -> tensor<1x16x32x2xbf16> { // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<16x32x2xbf16> - // CHECK: %[[PACK:.+]] = tensor.pack %[[ARG0]] inner_dims_pos = [0] inner_tiles = [2] into %[[EMPTY]] + // CHECK: %[[PACK:.+]] = linalg.pack %[[ARG0]] inner_dims_pos = [0] inner_tiles = [2] into %[[EMPTY]] // CHECK-SAME: : tensor<32x32xbf16> -> tensor<16x32x2xbf16> // CHECK: %[[EXP:.+]] = tensor.expand_shape %[[PACK]] {{\[}}[0, 1], [2], [3]] // CHECK-SAME: : tensor<16x32x2xbf16> into tensor<1x16x32x2xbf16> %expanded = tensor.expand_shape %arg0 [[0, 1], [2]] output_shape [1, 32, 32] : tensor<32x32xbf16> into tensor<1x32x32xbf16> - %pack = tensor.pack %expanded inner_dims_pos = [1] inner_tiles = [2] into %arg1 + %pack = linalg.pack %expanded inner_dims_pos = [1] inner_tiles = [2] into %arg1 : tensor<1x32x32xbf16> -> tensor<1x16x32x2xbf16> return %pack : tensor<1x16x32x2xbf16> } @@ -152,9 +152,9 @@ func.func @vnni_pack(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x16x32x2xb %3 = affine.apply #map(%arg2) %extracted_slice = tensor.extract_slice %arg0[%2, %3] [32, 32] [1, 1] : tensor<1024x512xbf16> to tensor<32x32xbf16> %extracted_slice_0 = tensor.extract_slice %0[%arg2, %arg3, 0, 0] [1, 1, 32, 32] [1, 1, 1, 1] : tensor<16x32x32x32xbf16> to tensor<1x1x32x32xbf16> - %pack = tensor.pack %extracted_slice outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %extracted_slice_0 : tensor<32x32xbf16> -> tensor<1x1x32x32xbf16> + %pack = linalg.pack %extracted_slice outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %extracted_slice_0 : tensor<32x32xbf16> -> tensor<1x1x32x32xbf16> %extracted_slice_1 = tensor.extract_slice %arg4[%arg2, %arg3, %c0, %c0, 0] [1, 1, %c16, %c32, 2] [1, 1, 1, 1, 1] : tensor<16x32x16x32x2xbf16> to tensor<1x1x?x?x2xbf16> - %pack_2 = tensor.pack %pack inner_dims_pos = [2] inner_tiles = [2] into %extracted_slice_1 : tensor<1x1x32x32xbf16> -> tensor<1x1x?x?x2xbf16> + %pack_2 = linalg.pack %pack inner_dims_pos = [2] inner_tiles = [2] into %extracted_slice_1 : tensor<1x1x32x32xbf16> -> tensor<1x1x?x?x2xbf16> scf.forall.in_parallel { tensor.parallel_insert_slice %pack_2 into %arg4[%arg2, %arg3, %c0, %c0, 0] [1, 1, %c16, %c32, 2] [1, 1, 1, 1, 1] : tensor<1x1x?x?x2xbf16> into tensor<16x32x16x32x2xbf16> } @@ -171,7 +171,7 @@ func.func @vnni_pack(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x16x32x2xb // CHECK: %[[SLICE:.+]] = tensor.extract_slice %arg0[%[[AFFINE_APPLY]], %[[AFFINE_APPLY_1]]] [32, 32] [1, 1] // CHECK-SAME: : tensor<1024x512xbf16> to tensor<32x32xbf16> // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<16x32x2xbf16> -// CHECK: %[[PACK:.+]] = tensor.pack %[[SLICE]] inner_dims_pos = [0] inner_tiles = [2] into %[[EMPTY]] +// CHECK: %[[PACK:.+]] = linalg.pack %[[SLICE]] inner_dims_pos = [0] inner_tiles = [2] into %[[EMPTY]] // CHECK-SAME: : tensor<32x32xbf16> -> tensor<16x32x2xbf16> // ----- @@ -179,7 +179,7 @@ func.func @vnni_pack(%arg0: tensor<1024x512xbf16>, %arg1: tensor<16x32x16x32x2xb func.func @fold_pack_in_insert_slice(%arg0: tensor<2x4x32x32xbf16>, %arg1: tensor<2x4x32x32xbf16>, %arg2: tensor<64x64xbf16>, %dest: tensor<64x64xbf16>) -> tensor<64x64xbf16> { %packed_layout = tensor.empty() : tensor<2x2x32x32xbf16> - %pack = tensor.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %packed_layout + %pack = linalg.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %packed_layout : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> %0 = scf.forall (%arg3, %arg4) in (2, 2) shared_outs(%arg5 = %pack) -> (tensor<2x2x32x32xbf16>) { %extracted_slice = tensor.extract_slice %arg0[%arg3, 0, 0, 0] [1, 4, 32, 32] [1, 1, 1, 1] : tensor<2x4x32x32xbf16> to tensor<4x32x32xbf16> @@ -190,7 +190,7 @@ func.func @fold_pack_in_insert_slice(%arg0: tensor<2x4x32x32xbf16>, %arg1: tenso tensor.parallel_insert_slice %4 into %arg5[%arg3, %arg4, 0, 0] [1, 1, 32, 32] [1, 1, 1, 1] : tensor<32x32xbf16> into tensor<2x2x32x32xbf16> } } - %unpack = tensor.unpack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest + %unpack = linalg.unpack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> return %unpack : tensor<64x64xbf16> } @@ -225,13 +225,13 @@ func.func @expect_to_fail_fold_pack_in_insert_slice( } } // We do not handle outer dims. - %unpack = tensor.unpack %0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest + %unpack = linalg.unpack %0 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> return %unpack : tensor<64x64xbf16> } // CHECK-LABEL: expect_to_fail_fold_pack_in_insert_slice -// CHECK: tensor.unpack +// CHECK: linalg.unpack // ----- @@ -248,13 +248,13 @@ func.func @expect_to_fail_fold_pack_in_insert_slice( tensor.parallel_insert_slice %4 into %arg6[%arg3, %arg4, 0, 0] [1, 1, 32, 32] [1, 1, 1, 1] : tensor<32x32xbf16> into tensor<2x2x32x32xbf16> } } - %unpack = tensor.unpack %0#1 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest + %unpack = linalg.unpack %0#1 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> return %unpack : tensor<64x64xbf16> } // CHECK-LABEL: expect_to_fail_fold_pack_in_insert_slice -// CHECK: tensor.unpack +// CHECK: linalg.unpack // ----- @@ -304,7 +304,7 @@ func.func @fold_pack_expect_to_fail_multiple_uses( tensor.parallel_insert_slice %4 into %arg5[%arg3, %arg4, 0, 0] [1, 1, 32, 32] [1, 1, 1, 1] : tensor<32x32xbf16> into tensor<2x2x32x32xbf16> } } - %unpack = tensor.unpack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest + %unpack = linalg.unpack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> %use = call @some_use(%0) : (tensor<2x2x32x32xbf16>) -> (tensor<64x64xbf16>) %add = linalg.add ins(%use, %unpack : tensor<64x64xbf16>, tensor<64x64xbf16>) @@ -313,7 +313,7 @@ func.func @fold_pack_expect_to_fail_multiple_uses( } // CHECK-LABEL: fold_pack_expect_to_fail_multiple_uses -// CHECK: tensor.unpack +// CHECK: linalg.unpack // ----- @@ -331,8 +331,8 @@ func.func @expect_to_fail_fold_pack_in_insert_slice_1( tensor.parallel_insert_slice %4 into %arg5[%arg3, %arg4, 0, 0] [1, 1, 32, 32] [1, 1, 1, 1] : tensor<32x32xbf16> into tensor<2x2x32x32xbf16> } } - // CHECK: tensor.unpack - %unpack = tensor.unpack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest + // CHECK: linalg.unpack + %unpack = linalg.unpack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> return %unpack : tensor<64x64xbf16> } @@ -343,7 +343,7 @@ func.func @expect_to_fold_pack_in_insert_slice_2( %arg0: tensor<2x4x32x32xbf16>, %arg1: tensor<2x4x32x32xbf16>, %arg2: tensor<64x64xbf16>, %dest: tensor<64x64xbf16>, %dest_t: tensor<2x2x32x32xbf16>) -> tensor<64x64xbf16> { %packed_layout = tensor.empty() : tensor<2x2x32x32xbf16> - %pack = tensor.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %packed_layout + %pack = linalg.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %packed_layout : tensor<64x64xbf16> -> tensor<2x2x32x32xbf16> %0:2 = scf.forall (%arg3, %arg4) in (2, 2) shared_outs(%arg5 = %dest_t, %arg6 = %pack) -> (tensor<2x2x32x32xbf16>, tensor<2x2x32x32xbf16>) { @@ -355,7 +355,7 @@ func.func @expect_to_fold_pack_in_insert_slice_2( tensor.parallel_insert_slice %4 into %arg6[%arg3, %arg4, 0, 0] [1, 1, 32, 32] [1, 1, 1, 1] : tensor<32x32xbf16> into tensor<2x2x32x32xbf16> } } - %unpack = tensor.unpack %0#1 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest + %unpack = linalg.unpack %0#1 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %dest : tensor<2x2x32x32xbf16> -> tensor<64x64xbf16> return %unpack : tensor<64x64xbf16> } @@ -366,7 +366,7 @@ func.func @expect_to_fold_pack_in_insert_slice_2( // CHECK-SAME: %[[ARG0:.+]]: tensor<2x4x32x32xbf16>, %[[ARG1:.+]]: tensor<2x4x32x32xbf16>, // CHECK-SAME: %[[ARG2:.+]]: tensor<64x64xbf16>, %[[ARG3:.+]]: tensor<64x64xbf16> // CHECK-SAME: %[[ARG4:.+]]: tensor<2x2x32x32xbf16> -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: %[[RES:.+]] = scf.forall (%[[ARG5:.+]], %[[ARG6:.+]]) in (2, 2) shared_outs(%[[ARG7:.+]] = %[[ARG2]]) // CHECK: %[[AFFINE_I:.+]] = affine.apply #[[MAP]](%[[ARG5]]) // CHECK: %[[AFFINE_J:.+]] = affine.apply #[[MAP]](%[[ARG6]]) @@ -376,5 +376,5 @@ func.func @expect_to_fold_pack_in_insert_slice_2( // CHECK: %[[GEMM:.+]] = linalg.batch_reduce_matmul ins(%[[SLICE]], %[[SLICE_0]] : tensor<4x32x32xbf16>, tensor<4x32x32xbf16>) // CHECK-SAME: outs(%[[SLICE_1]] : tensor<32x32xbf16>) -> tensor<32x32xbf16> // CHECK: tensor.parallel_insert_slice %[[GEMM]] into %[[ARG7]][%[[AFFINE_I]], %[[AFFINE_J]]] [32, 32] [1, 1] : tensor<32x32xbf16> into tensor<64x64xbf16> -// CHECK-NOT: tensor.unpack +// CHECK-NOT: linalg.unpack // CHECK: return %[[RES]] : tensor<64x64xbf16> diff --git a/test/Passes/tpp-mapping.mlir b/test/Passes/tpp-mapping.mlir index 29db80d2a..e42b2799e 100644 --- a/test/Passes/tpp-mapping.mlir +++ b/test/Passes/tpp-mapping.mlir @@ -40,14 +40,14 @@ func.func @conv_2d_nhwc_hwcf(%arg0: tensor<1x113x113x64xf32>, %arg1: tensor<3x3x // CHECK-DAG: %[[C8:.+]] = arith.constant 8 : index // CHECK-DAG: %[[C111:.+]] = arith.constant 111 : index // Conv as matmul -// CHECK-COUNT-3: tensor.pack +// CHECK-COUNT-3: linalg.pack // CHECK: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C8]] step %[[C1]] // CHECK-NEXT: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C111]] step %[[C1]] // CHECK-NEXT: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C2]] step %[[C1]] // CHECK-NEXT: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C3]] step %[[C1]] // CHECK-NEXT: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C3]] step %[[C1]] // CHECK: linalg.matmul -// CHECK: tensor.unpack +// CHECK: linalg.unpack // ----- @@ -65,13 +65,13 @@ func.func @conv_2d_nchw_fchw(%i: tensor<14x512x28x28xf32>, %f: tensor<1024x512x1 // CHECK-DAG: %[[C14:.+]] = arith.constant 14 : index // CHECK-DAG: %[[C16:.+]] = arith.constant 16 : index // CHECK-DAG: %[[C28:.+]] = arith.constant 28 : index -// CHECK-COUNT-3: tensor.pack +// CHECK-COUNT-3: linalg.pack // CHECK: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C14]] step %[[C1]] // CHECK-NEXT: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C32]] step %[[C1]] // CHECK-NEXT: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C28]] step %[[C1]] // CHECK-NEXT: %{{.+}} = scf.for %{{.+}} = %[[C0]] to %[[C16]] step %[[C1]] // CHECK: linalg.matmul -// CHECK: tensor.unpack +// CHECK: linalg.unpack // ----- @@ -96,7 +96,7 @@ func.func @pack_matmul( } // CHECK-LABEL: pack_matmul -// CHECK-COUNT-2: tensor.pack +// CHECK-COUNT-2: linalg.pack // Packed matmul // CHECK: %{{.+}} = scf.forall (%{{.+}}, %{{.+}}) in (4, 4) // CHECK: %{{.+}} = linalg.batch_reduce_matmul ins(%{{.+}}, %{{.+}} : tensor<4x32x32xf32>, tensor<4x32x32xf32>) @@ -107,12 +107,12 @@ func.func @pack_matmul( func.func @fold_const_pack() -> tensor<8x2x1x1x32x32xi64> { %cst = arith.constant dense<1> : tensor<1x1x64x256xi64> %0 = tensor.empty() : tensor<8x2x1x1x32x32xi64> - %pack = tensor.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %0 : tensor<1x1x64x256xi64> -> tensor<8x2x1x1x32x32xi64> + %pack = linalg.pack %cst outer_dims_perm = [3, 2, 0, 1] inner_dims_pos = [2, 3] inner_tiles = [32, 32] into %0 : tensor<1x1x64x256xi64> -> tensor<8x2x1x1x32x32xi64> return %pack : tensor<8x2x1x1x32x32xi64> } // CHECK-LABEL: func.func @fold_const_pack( -// CHECK-NOT: tensor.pack +// CHECK-NOT: linalg.pack // CHECK: %[[CST:.+]] = arith.constant dense<1> : tensor<8x2x1x1x32x32xi64> // CHECK-NEXT: return %[[CST]] : tensor<8x2x1x1x32x32xi64> @@ -126,18 +126,18 @@ func.func @fold_const_pack() -> tensor<8x2x1x1x32x32xi64> { func.func @propagate_pack_unpack(%arg0: tensor<128x512xf32>, %arg1: tensor<512x256xf32>, %arg2: tensor<128x256xf32>) -> tensor<128x256xf32> { %cst = arith.constant 0.000000e+00 : f32 %0 = tensor.empty() : tensor<4x16x32x32xf32> - %pack = tensor.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> + %pack = linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %0 : tensor<128x512xf32> -> tensor<4x16x32x32xf32> %1 = tensor.empty() : tensor<8x16x32x32xf32> - %pack_0 = tensor.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> + %pack_0 = linalg.pack %arg1 outer_dims_perm = [1, 0] inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %1 : tensor<512x256xf32> -> tensor<8x16x32x32xf32> %2 = tensor.empty() : tensor<4x8x32x32xf32> - %pack_1 = tensor.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> + %pack_1 = linalg.pack %arg2 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %2 : tensor<128x256xf32> -> tensor<4x8x32x32xf32> %3 = linalg.generic {indexing_maps = [#map, #map1, #map2], iterator_types = ["parallel", "parallel", "reduction", "parallel", "parallel", "reduction"]} ins(%pack, %pack_0 : tensor<4x16x32x32xf32>, tensor<8x16x32x32xf32>) outs(%pack_1 : tensor<4x8x32x32xf32>) { ^bb0(%in: f32, %in_2: f32, %out: f32): %5 = arith.mulf %in, %in_2 : f32 %6 = arith.addf %out, %5 : f32 linalg.yield %6 : f32 } -> tensor<4x8x32x32xf32> - %unpack = tensor.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> + %unpack = linalg.unpack %3 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg2 : tensor<4x8x32x32xf32> -> tensor<128x256xf32> %4 = linalg.generic {indexing_maps = [#map3], iterator_types = ["parallel", "parallel"]} outs(%unpack : tensor<128x256xf32>) { ^bb0(%out: f32): %5 = arith.maximumf %out, %cst : f32 @@ -148,7 +148,7 @@ func.func @propagate_pack_unpack(%arg0: tensor<128x512xf32>, %arg1: tensor<512x2 // CHECK-LABEL: propagate_pack_unpack // CHECK: linalg.batch_reduce_matmul -// CHECK-NOT: tensor.unpack +// CHECK-NOT: linalg.unpack // CHECK: linalg.generic // ----- @@ -201,7 +201,7 @@ func.func @tile_and_fuse(%arg0: tensor<64x64xf32>, %arg1: tensor<64x64xf32>, } // CHECK-LABEL: tile_and_fuse( -// CHECK-COUNT-2: tensor.pack +// CHECK-COUNT-2: linalg.pack // Fused matmul and relu // CHECK: scf.forall // CHECK: linalg.batch_reduce_matmul{{.*}}ins(%{{.+}}, %{{.+}} : tensor<2x32x32xf32>, tensor<2x32x32xf32>) diff --git a/test/TestLib/TestForToForAllRewrite.cpp b/test/TestLib/TestForToForAllRewrite.cpp index 278d7e54c..e6b08bbc7 100644 --- a/test/TestLib/TestForToForAllRewrite.cpp +++ b/test/TestLib/TestForToForAllRewrite.cpp @@ -31,7 +31,7 @@ struct TestForToForAllRewrite void TestForToForAllRewrite::runOnOperation() { RewritePatternSet patterns(&getContext()); linalgx::utils::populateScfForToForAllRewritePattern(patterns); - (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); + (void)applyPatternsGreedily(getOperation(), std::move(patterns)); } namespace mlir { diff --git a/tools/tpp-run/README.md b/tools/tpp-run/README.md index 307a62cca..32e9e1a5f 100644 --- a/tools/tpp-run/README.md +++ b/tools/tpp-run/README.md @@ -1,6 +1,6 @@ # TPP Runner -This is basically a copy of `mlir-cpu-runner`, using `JitRunnerMain` and its call backs. +This is basically a copy of `mlir-runner`, using `JitRunnerMain` and its call backs. The main difference is that we add a wrapper function to call the kernel (entry) function to allow for benchmarking. @@ -31,12 +31,12 @@ If we add new callbacks, we must upstream this. ## Entry Point -Just like `mlir-cpu-runner`, `tpp-run` is supposed to work with `tpp-opt`, `mlir-opt`, etc. +Just like `mlir-runner`, `tpp-run` is supposed to work with `tpp-opt`, `mlir-opt`, etc. However, it also introduces MLIR functions, so it has some internal passes to convert those to LLVM, and it requires the original functions *not* to be in the LLVM Dialect. For these reasons, the entry point of `tpp-run` is _"after all code-gen passes of the optimizer"_ and _"just before the first LLVM lowering"_. -So, if in `mlir-opt` you'd pass LLVM lowering flags to run on `mlir-cpu-runner`, with `tpp-opt`, you cannot. +So, if in `mlir-opt` you'd pass LLVM lowering flags to run on `mlir-runner`, with `tpp-opt`, you cannot. All other passes, however, even including partial conversions (ex. `scf-to-cf`) need to be passed, as we can't assume what the original IR had used. This may change in the future when the program gets more complex, but for now, it's a safe point. diff --git a/tools/tpp-run/tpp-run.cpp b/tools/tpp-run/tpp-run.cpp index 7a2a7d7f1..753dc907f 100644 --- a/tools/tpp-run/tpp-run.cpp +++ b/tools/tpp-run/tpp-run.cpp @@ -192,7 +192,7 @@ static LogicalResult prepareMLIRKernel(Operation *op, std::unique_ptr lowerToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext) { - // Default lowering for mlir-cpu-runner + // Default lowering for mlir-runner auto llvmModule = translateModuleToLLVMIR(module, llvmContext); assert(llvmModule);