From cd58c8da09dda06da13a64290075733ab79d8834 Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 11 Sep 2024 15:04:26 +0300 Subject: [PATCH 01/17] Add workflow --- .github/workflows/test-localnet.yml | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/test-localnet.yml diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml new file mode 100644 index 00000000..d7a2a2fc --- /dev/null +++ b/.github/workflows/test-localnet.yml @@ -0,0 +1,61 @@ +name: MultiversX Integration Tests + +on: + push: + branches: + - main + pull_request: + +jobs: + integration_tests: + runs-on: ubuntu-latest + + steps: + # Step 1: Checkout the repository + - name: Checkout code + uses: actions/checkout@v3 + + # Step 2: Set up Python environment + - name: Set up Python 3.x + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + # Step 3: Install mxpy (MultiversX Python SDK) + - name: Install mxpy (MultiversX SDK) + run: | + pip install mx-sdk-cli + + # Step 4: Set up MultiversX localnet using mxpy + - name: Set up MultiversX localnet + run: | + # Start the local testnet with mxpy + mkdir -p ~/localnet && cd ~/localnet + mxpy localnet setup + mxpy localnet start + sleep 60 # Allow time for the testnet to fully start + + # Step 5: Verify that testnet is running using mxpy CLI + - name: Verify localnet running + run: | + mxpy localnet status + + # Step 6: Install Node.js and dependencies + - name: Set up Node.js environment + uses: actions/setup-node@v3 + with: + node-version: '16.x' + + - name: Install Node.js dependencies + run: npm install + + # Step 7: Run integration tests + - name: Run integration tests + run: | + npm run tests-localnet + + # Step 8: Stop the testnet + - name: Stop MultiversX local localnet + if: success() || failure() + run: | + mxpy localnet stop From 865ef1e982a3a043ddee89ddd2f23725b13833be Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 11 Sep 2024 15:06:15 +0300 Subject: [PATCH 02/17] Fix mxpy install --- .github/workflows/test-localnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index d7a2a2fc..d5715e8c 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -24,7 +24,7 @@ jobs: # Step 3: Install mxpy (MultiversX Python SDK) - name: Install mxpy (MultiversX SDK) run: | - pip install mx-sdk-cli + pip install mx-sdk-cli --force # Step 4: Set up MultiversX localnet using mxpy - name: Set up MultiversX localnet From 04a18763ea15030038959a9261a8231abba814cc Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 11 Sep 2024 15:10:33 +0300 Subject: [PATCH 03/17] Add pipx instalation step --- .github/workflows/test-localnet.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index d5715e8c..ae33943e 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -21,10 +21,17 @@ jobs: with: python-version: '3.x' + # Step 2: Set up Python environment + - name: Set up pipx + run: | + apt update + apt install pipx + pipx ensurepath + # Step 3: Install mxpy (MultiversX Python SDK) - name: Install mxpy (MultiversX SDK) run: | - pip install mx-sdk-cli --force + pipx install multiversx-sdk-cli --force # Step 4: Set up MultiversX localnet using mxpy - name: Set up MultiversX localnet From ba0a321d2c19dcb1f53acc040843b314aca238c1 Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 11 Sep 2024 15:13:26 +0300 Subject: [PATCH 04/17] Fix pipx instalation --- .github/workflows/test-localnet.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index ae33943e..e1ac4f73 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -21,12 +21,14 @@ jobs: with: python-version: '3.x' - # Step 2: Set up Python environment - - name: Set up pipx + # Step 3: Install pipx (to manage Python tools) + - name: Install pipx run: | - apt update - apt install pipx - pipx ensurepath + python3 -m pip install --user pipx + python3 -m pipx ensurepath + # Add the pipx binary location to PATH + echo "$HOME/.local/bin" >> $GITHUB_PATH + shell: bash # Step 3: Install mxpy (MultiversX Python SDK) - name: Install mxpy (MultiversX SDK) From 6f61e602932de56d52f1b8994b60098de0515b92 Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 11 Sep 2024 15:18:43 +0300 Subject: [PATCH 05/17] Remove verify step --- .github/workflows/test-localnet.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index e1ac4f73..ed465ff7 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -30,12 +30,12 @@ jobs: echo "$HOME/.local/bin" >> $GITHUB_PATH shell: bash - # Step 3: Install mxpy (MultiversX Python SDK) + # Step 4: Install mxpy (MultiversX Python SDK) - name: Install mxpy (MultiversX SDK) run: | pipx install multiversx-sdk-cli --force - # Step 4: Set up MultiversX localnet using mxpy + # Step 5: Set up MultiversX localnet using mxpy - name: Set up MultiversX localnet run: | # Start the local testnet with mxpy @@ -44,11 +44,6 @@ jobs: mxpy localnet start sleep 60 # Allow time for the testnet to fully start - # Step 5: Verify that testnet is running using mxpy CLI - - name: Verify localnet running - run: | - mxpy localnet status - # Step 6: Install Node.js and dependencies - name: Set up Node.js environment uses: actions/setup-node@v3 From 3cb06dab6cf1c886c2cf72f2c749027cad971e2f Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 11 Sep 2024 15:27:17 +0300 Subject: [PATCH 06/17] Add nohup --- .github/workflows/test-localnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index ed465ff7..c204589c 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -41,7 +41,7 @@ jobs: # Start the local testnet with mxpy mkdir -p ~/localnet && cd ~/localnet mxpy localnet setup - mxpy localnet start + nohup mxpy localnet start sleep 60 # Allow time for the testnet to fully start # Step 6: Install Node.js and dependencies From 473e284af8c24c95ebbed6a6317f86921603706e Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 11 Sep 2024 15:32:47 +0300 Subject: [PATCH 07/17] Fix start localnet --- .github/workflows/test-localnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index c204589c..2f180234 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -41,7 +41,7 @@ jobs: # Start the local testnet with mxpy mkdir -p ~/localnet && cd ~/localnet mxpy localnet setup - nohup mxpy localnet start + nohup mxpy localnet start & sleep 60 # Allow time for the testnet to fully start # Step 6: Install Node.js and dependencies From 8ee4554a547bd554e35c63ccd346e9b3e41bdc25 Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 12 Sep 2024 15:16:43 +0300 Subject: [PATCH 08/17] Add managed decimal signed codec --- src/smartcontracts/codec/binary.ts | 13 ++++ src/smartcontracts/codec/managedDecimal.ts | 15 ++-- .../codec/managedDecimalSigned.ts | 56 ++++++++++++++ .../interaction.local.net.spec.ts | 17 ++--- src/smartcontracts/typesystem/index.ts | 1 + .../typesystem/managedDecimal.ts | 65 ---------------- .../typesystem/managedDecimalSigned.ts | 71 ++++++++++++++++++ src/smartcontracts/typesystem/matchers.ts | 10 +++ src/smartcontracts/typesystem/typeMapper.ts | 3 +- src/smartcontracts/typesystem/types.spec.ts | 3 +- src/testdata/basic-features.abi.json | 12 +-- src/testdata/basic-features.wasm | Bin 87521 -> 66700 bytes 12 files changed, 176 insertions(+), 90 deletions(-) create mode 100644 src/smartcontracts/codec/managedDecimalSigned.ts create mode 100644 src/smartcontracts/typesystem/managedDecimalSigned.ts mode change 100644 => 100755 src/testdata/basic-features.wasm diff --git a/src/smartcontracts/codec/binary.ts b/src/smartcontracts/codec/binary.ts index eb52e9d0..0c5bbdb2 100644 --- a/src/smartcontracts/codec/binary.ts +++ b/src/smartcontracts/codec/binary.ts @@ -18,6 +18,8 @@ import { ArrayVec, ManagedDecimalType, ManagedDecimalValue, + ManagedDecimalSignedType, + ManagedDecimalSignedValue, } from "../typesystem"; import { guardTrue } from "../../utils"; import { OptionValueBinaryCodec } from "./option"; @@ -28,6 +30,7 @@ import { EnumBinaryCodec } from "./enum"; import { TupleBinaryCodec } from "./tuple"; import { ArrayVecBinaryCodec } from "./arrayVec"; import { ManagedDecimalCodec } from "./managedDecimal"; +import { ManagedDecimalSignedCodec } from "./managedDecimalSigned"; export class BinaryCodec { readonly constraints: BinaryCodecConstraints; @@ -39,6 +42,7 @@ export class BinaryCodec { private readonly tupleCodec: TupleBinaryCodec; private readonly enumCodec: EnumBinaryCodec; private readonly managedDecimalCodec: ManagedDecimalCodec; + private readonly managedDecimalSignedCodec: ManagedDecimalSignedCodec; constructor(constraints: BinaryCodecConstraints | null = null) { this.constraints = constraints || new BinaryCodecConstraints(); @@ -50,6 +54,7 @@ export class BinaryCodec { this.tupleCodec = new TupleBinaryCodec(this); this.enumCodec = new EnumBinaryCodec(this); this.managedDecimalCodec = new ManagedDecimalCodec(this); + this.managedDecimalSignedCodec = new ManagedDecimalSignedCodec(this); } decodeTopLevel(buffer: Buffer, type: Type): TResult { @@ -64,6 +69,8 @@ export class BinaryCodec { onTuple: () => this.tupleCodec.decodeTopLevel(buffer, type), onEnum: () => this.enumCodec.decodeTopLevel(buffer, type), onManagedDecimal: () => this.managedDecimalCodec.decodeTopLevel(buffer, type), + onManagedDecimalSigned: () => + this.managedDecimalSignedCodec.decodeTopLevel(buffer, type), }); return typedValue; @@ -81,6 +88,8 @@ export class BinaryCodec { onTuple: () => this.tupleCodec.decodeNested(buffer, type), onEnum: () => this.enumCodec.decodeNested(buffer, type), onManagedDecimal: () => this.managedDecimalCodec.decodeNested(buffer, type), + onManagedDecimalSigned: () => + this.managedDecimalSignedCodec.decodeNested(buffer, type), }); return [typedResult, decodedLength]; @@ -98,6 +107,8 @@ export class BinaryCodec { onTuple: () => this.tupleCodec.encodeNested(typedValue), onEnum: () => this.enumCodec.encodeNested(typedValue), onManagedDecimal: () => this.managedDecimalCodec.encodeNested(typedValue), + onManagedDecimalSigned: () => + this.managedDecimalSignedCodec.encodeNested(typedValue), }); } @@ -113,6 +124,8 @@ export class BinaryCodec { onTuple: () => this.tupleCodec.encodeTopLevel(typedValue), onEnum: () => this.enumCodec.encodeTopLevel(typedValue), onManagedDecimal: () => this.managedDecimalCodec.encodeTopLevel(typedValue), + onManagedDecimalSigned: () => + this.managedDecimalSignedCodec.encodeTopLevel(typedValue), }); } } diff --git a/src/smartcontracts/codec/managedDecimal.ts b/src/smartcontracts/codec/managedDecimal.ts index 1a0e1c1c..0718dfa7 100644 --- a/src/smartcontracts/codec/managedDecimal.ts +++ b/src/smartcontracts/codec/managedDecimal.ts @@ -1,5 +1,5 @@ import BigNumber from "bignumber.js"; -import { BigUIntValue, ManagedDecimalType, ManagedDecimalValue, U32Value } from "../typesystem"; +import { BigUIntType, BigUIntValue, ManagedDecimalType, ManagedDecimalValue, U32Value } from "../typesystem"; import { BinaryCodec } from "./binary"; import { bufferToBigInt } from "./utils"; import { SizeOfU32 } from "./constants"; @@ -27,16 +27,15 @@ export class ManagedDecimalCodec { if (type.isVariable()) { const bigUintSize = buffer.length - SizeOfU32; - const value = new BigNumber(buffer.slice(0, bigUintSize).toString("hex"), 16); + const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigUIntType()); const scale = buffer.readUInt32BE(bigUintSize); - - return new ManagedDecimalValue(value, scale); + return new ManagedDecimalValue(value.valueOf(), scale); } const value = bufferToBigInt(buffer); const metadata = type.getMetadata(); - const scale = typeof metadata === "number" ? metadata : 0; - return new ManagedDecimalValue(value, scale); + const scale = metadata !== "usize" ? parseInt(metadata.toString()) : 0; + return new ManagedDecimalValue(value.shiftedBy(-scale), scale); } encodeNested(value: ManagedDecimalValue): Buffer { @@ -45,7 +44,9 @@ export class ManagedDecimalCodec { buffers.push(Buffer.from(this.binaryCodec.encodeNested(new BigUIntValue(value.valueOf())))); buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale())))); } else { - buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(new BigUIntValue(value.valueOf())))); + buffers.push( + this.binaryCodec.encodeTopLevel(new BigUIntValue(value.valueOf().shiftedBy(value.getScale()))), + ); } return Buffer.concat(buffers); } diff --git a/src/smartcontracts/codec/managedDecimalSigned.ts b/src/smartcontracts/codec/managedDecimalSigned.ts new file mode 100644 index 00000000..f6e0654d --- /dev/null +++ b/src/smartcontracts/codec/managedDecimalSigned.ts @@ -0,0 +1,56 @@ +import BigNumber from "bignumber.js"; +import { BigIntType, BigIntValue, ManagedDecimalSignedType, ManagedDecimalSignedValue, U32Value } from "../typesystem"; +import { BinaryCodec } from "./binary"; +import { bufferToBigInt } from "./utils"; +import { SizeOfU32 } from "./constants"; + +export class ManagedDecimalSignedCodec { + private readonly binaryCodec: BinaryCodec; + + constructor(binaryCodec: BinaryCodec) { + this.binaryCodec = binaryCodec; + } + + decodeNested(buffer: Buffer, type: ManagedDecimalSignedType): [ManagedDecimalSignedValue, number] { + const length = buffer.readUInt32BE(0); + const payload = buffer.slice(0, length); + + const result = this.decodeTopLevel(payload, type); + return [result, length]; + } + + decodeTopLevel(buffer: Buffer, type: ManagedDecimalSignedType): ManagedDecimalSignedValue { + if (buffer.length === 0) { + return new ManagedDecimalSignedValue(new BigNumber(0), 0); + } + + if (type.isVariable()) { + const bigUintSize = buffer.length - SizeOfU32; + + const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigIntType()); + const scale = buffer.readUInt32BE(bigUintSize); + + return new ManagedDecimalSignedValue(value.valueOf(), scale); + } + + const value = bufferToBigInt(buffer); + const metadata = type.getMetadata(); + const scale = metadata !== "usize" ? parseInt(metadata.toString()) : 0; + return new ManagedDecimalSignedValue(value.shiftedBy(-scale), scale); + } + + encodeNested(value: ManagedDecimalSignedValue): Buffer { + let buffers: Buffer[] = []; + if (value.isVariable()) { + buffers.push(Buffer.from(this.binaryCodec.encodeNested(new BigIntValue(new BigNumber("23.000000000"))))); + buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale())))); + } else { + buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(new BigIntValue(23.0)))); + } + return Buffer.concat(buffers); + } + + encodeTopLevel(value: ManagedDecimalSignedValue): Buffer { + return this.encodeNested(value); + } +} diff --git a/src/smartcontracts/interaction.local.net.spec.ts b/src/smartcontracts/interaction.local.net.spec.ts index db572bfd..13e0780e 100644 --- a/src/smartcontracts/interaction.local.net.spec.ts +++ b/src/smartcontracts/interaction.local.net.spec.ts @@ -185,7 +185,7 @@ describe("test smart contract interactor", function () { assert.isTrue(typedBundle.returnCode.equals(ReturnCode.Ok)); }); - it("should interact with 'basic-features' (local testnet) using the SmartContractTransactionsFactory", async function () { + it("should interact with 'basic-features' (local testnet)", async function () { this.timeout(140000); let abiRegistry = await loadAbiRegistry("src/testdata/basic-features.abi.json"); @@ -226,13 +226,13 @@ describe("test smart contract interactor", function () { .buildTransaction(); let additionInteraction = contract.methods - .managed_decimal_addition([new ManagedDecimalValue(2, 2), new ManagedDecimalValue(3, 2)]) + .managed_decimal_addition([new ManagedDecimalValue("2.5", 2), new ManagedDecimalValue("2.5", 2)]) .withGasLimit(10000000) .withChainID(network.ChainID) .withSender(alice.address) .withValue(0); - // addition() + // addition(); let additionTransaction = additionInteraction .withSender(alice.address) .useThenIncrementNonceOf(alice.account) @@ -240,7 +240,7 @@ describe("test smart contract interactor", function () { // log let mdLnInteraction = contract.methods - .managed_decimal_ln([new ManagedDecimalValue(23000000000, 9)]) + .managed_decimal_ln([new ManagedDecimalValue(23, 9)]) .withGasLimit(10000000) .withChainID(network.ChainID) .withSender(alice.address) @@ -253,10 +253,7 @@ describe("test smart contract interactor", function () { .buildTransaction(); let additionVarInteraction = contract.methods - .managed_decimal_addition_var([ - new ManagedDecimalValue(378298000000, 9, true), - new ManagedDecimalValue(378298000000, 9, true), - ]) + .managed_decimal_addition_var([new ManagedDecimalValue(4, 2, true), new ManagedDecimalValue(5, 2, true)]) .withGasLimit(50000000) .withChainID(network.ChainID) .withSender(alice.address) @@ -287,14 +284,14 @@ describe("test smart contract interactor", function () { let { bundle: bundleMDLn } = await controller.execute(mdLnInteraction, mdLnTransaction); assert.isTrue(bundleMDLn.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleMDLn.values, 1); - assert.deepEqual(bundleMDLn.values[0], new ManagedDecimalSignedValue(3135553845, 9)); + assert.deepEqual(bundleMDLn.values[0], new ManagedDecimalSignedValue(3.135553845, 9)); // addition with var decimals await signTransaction({ transaction: additionVarTransaction, wallet: alice }); let { bundle: bundleAddition } = await controller.execute(additionVarInteraction, additionVarTransaction); assert.isTrue(bundleAddition.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleAddition.values, 1); - assert.deepEqual(bundleAddition.values[0], new ManagedDecimalValue(new BigNumber(6254154138880), 9)); + assert.deepEqual(bundleAddition.values[0], new ManagedDecimalValue(9, 2)); }); it("should interact with 'counter' (local testnet)", async function () { diff --git a/src/smartcontracts/typesystem/index.ts b/src/smartcontracts/typesystem/index.ts index a2cadaa0..7f5dd2da 100644 --- a/src/smartcontracts/typesystem/index.ts +++ b/src/smartcontracts/typesystem/index.ts @@ -29,3 +29,4 @@ export * from "./typeMapper"; export * from "./types"; export * from "./variadic"; export * from "./managedDecimal"; +export * from "./managedDecimalSigned"; diff --git a/src/smartcontracts/typesystem/managedDecimal.ts b/src/smartcontracts/typesystem/managedDecimal.ts index 1522c962..b65b26a4 100644 --- a/src/smartcontracts/typesystem/managedDecimal.ts +++ b/src/smartcontracts/typesystem/managedDecimal.ts @@ -69,68 +69,3 @@ export class ManagedDecimalValue extends TypedValue { return this.variable; } } - -export class ManagedDecimalSignedType extends Type { - static ClassName = "ManagedDecimalSignedType"; - - constructor(metadata: number | "usize") { - super("ManagedDecimalSigned", undefined, undefined, metadata); - } - - getClassName(): string { - return ManagedDecimalType.ClassName; - } - - getMetadata(): number | "usize" { - return this.metadata; - } - - isVariable(): boolean { - return this.metadata == "usize"; - } -} - -export class ManagedDecimalSignedValue extends TypedValue { - static ClassName = "ManagedDecimalSignedValue"; - private readonly value: BigNumber; - private readonly scale: number; - private readonly variable: boolean; - - constructor(value: BigNumber.Value, scale: number, isVariable: boolean = false) { - super(new ManagedDecimalSignedType(isVariable ? "usize" : scale)); - this.value = new BigNumber(value); - this.scale = scale; - this.variable = isVariable; - } - - getClassName(): string { - return ManagedDecimalValue.ClassName; - } - - getPrecision(): number { - return this.value.toFixed(this.scale).replace(".", "").length; - } - - /** - * Returns whether two objects have the same value. - */ - equals(other: ManagedDecimalSignedValue): boolean { - if (this.getPrecision() != other.getPrecision()) { - return false; - } - - return new BigNumber(this.value).eq(other.value); - } - - valueOf(): BigNumber { - return this.value; - } - - toString(): string { - return this.value.toFixed(this.scale); - } - - isVariable(): boolean { - return this.variable; - } -} diff --git a/src/smartcontracts/typesystem/managedDecimalSigned.ts b/src/smartcontracts/typesystem/managedDecimalSigned.ts new file mode 100644 index 00000000..032626f0 --- /dev/null +++ b/src/smartcontracts/typesystem/managedDecimalSigned.ts @@ -0,0 +1,71 @@ +import BigNumber from "bignumber.js"; +import { Type, TypedValue } from "./types"; + +export class ManagedDecimalSignedType extends Type { + static ClassName = "ManagedDecimalSignedType"; + + constructor(metadata: number | "usize") { + super("ManagedDecimalSigned", undefined, undefined, metadata); + } + + getClassName(): string { + return ManagedDecimalSignedType.ClassName; + } + + getMetadata(): number | "usize" { + return this.metadata; + } + + isVariable(): boolean { + return this.metadata == "usize"; + } +} + +export class ManagedDecimalSignedValue extends TypedValue { + static ClassName = "ManagedDecimalSignedValue"; + private readonly value: BigNumber; + private readonly scale: number; + private readonly variable: boolean; + + constructor(value: BigNumber.Value, scale: number, isVariable: boolean = false) { + super(new ManagedDecimalSignedType(isVariable ? "usize" : scale)); + this.value = new BigNumber(value); + this.scale = scale; + this.variable = isVariable; + } + + getClassName(): string { + return ManagedDecimalSignedValue.ClassName; + } + + getPrecision(): number { + return this.value.toFixed(this.scale).replace(".", "").length; + } + + getScale(): number { + return this.scale; + } + + /** + * Returns whether two objects have the same value. + */ + equals(other: ManagedDecimalSignedValue): boolean { + if (this.getPrecision() != other.getPrecision()) { + return false; + } + + return new BigNumber(this.value).eq(other.value); + } + + valueOf(): BigNumber { + return this.value; + } + + toString(): string { + return this.value.toFixed(this.scale); + } + + isVariable(): boolean { + return this.variable; + } +} diff --git a/src/smartcontracts/typesystem/matchers.ts b/src/smartcontracts/typesystem/matchers.ts index 4bf5280a..7a901d75 100644 --- a/src/smartcontracts/typesystem/matchers.ts +++ b/src/smartcontracts/typesystem/matchers.ts @@ -16,6 +16,7 @@ import { ArrayVec, ArrayVecType } from "./genericArray"; import { TypedValue } from "./types"; import { StringType, StringValue } from "./string"; import { ManagedDecimalType, ManagedDecimalValue } from "./managedDecimal"; +import { ManagedDecimalSignedType, ManagedDecimalSignedValue } from "./managedDecimalSigned"; // TODO: Extend functionality or rename wrt. restricted / reduced functionality (not all types are handled: composite, variadic). export function onTypeSelect( @@ -29,6 +30,7 @@ export function onTypeSelect( onTuple: () => TResult; onEnum: () => TResult; onManagedDecimal: () => TResult; + onManagedDecimalSigned: () => TResult; onOther?: () => TResult; }, ): TResult { @@ -58,6 +60,10 @@ export function onTypeSelect( return selectors.onManagedDecimal(); } + if (type.hasExactClass(ManagedDecimalSignedType.ClassName)) { + return selectors.onManagedDecimalSigned(); + } + if (selectors.onOther) { return selectors.onOther(); } @@ -76,6 +82,7 @@ export function onTypedValueSelect( onTuple: () => TResult; onEnum: () => TResult; onManagedDecimal: () => TResult; + onManagedDecimalSigned: () => TResult; onOther?: () => TResult; }, ): TResult { @@ -103,6 +110,9 @@ export function onTypedValueSelect( if (value.hasExactClass(ManagedDecimalValue.ClassName)) { return selectors.onManagedDecimal(); } + if (value.hasExactClass(ManagedDecimalSignedValue.ClassName)) { + return selectors.onManagedDecimalSigned(); + } if (selectors.onOther) { return selectors.onOther(); diff --git a/src/smartcontracts/typesystem/typeMapper.ts b/src/smartcontracts/typesystem/typeMapper.ts index f058bacc..e8ca9a4d 100644 --- a/src/smartcontracts/typesystem/typeMapper.ts +++ b/src/smartcontracts/typesystem/typeMapper.ts @@ -10,7 +10,8 @@ import { FieldDefinition } from "./fields"; import { ListType, OptionType } from "./generic"; import { ArrayVecType } from "./genericArray"; import { H256Type } from "./h256"; -import { ManagedDecimalSignedType, ManagedDecimalType } from "./managedDecimal"; +import { ManagedDecimalType } from "./managedDecimal"; +import { ManagedDecimalSignedType } from "./managedDecimalSigned"; import { NothingType } from "./nothing"; import { BigIntType, diff --git a/src/smartcontracts/typesystem/types.spec.ts b/src/smartcontracts/typesystem/types.spec.ts index 06d70e52..eb77985a 100644 --- a/src/smartcontracts/typesystem/types.spec.ts +++ b/src/smartcontracts/typesystem/types.spec.ts @@ -9,7 +9,8 @@ import { I64Type, NumericalValue, U16Type, U32Type, U32Value } from "./numerical import { StringType } from "./string"; import { TypeExpressionParser } from "./typeExpressionParser"; import { NullType, PrimitiveType, Type } from "./types"; -import { ManagedDecimalSignedType, ManagedDecimalType } from "./managedDecimal"; +import { ManagedDecimalType } from "./managedDecimal"; +import { ManagedDecimalSignedType } from "./managedDecimalSigned"; describe("test types", () => { let parser = new TypeExpressionParser(); diff --git a/src/testdata/basic-features.abi.json b/src/testdata/basic-features.abi.json index 762e044c..f30c5500 100644 --- a/src/testdata/basic-features.abi.json +++ b/src/testdata/basic-features.abi.json @@ -1,20 +1,20 @@ { "buildInfo": { "rustc": { - "version": "1.80.1", - "commitHash": "3f5fd8dd41153bc5fdca9427e9e05be2c767ba23", - "commitDate": "2024-08-06", + "version": "1.80.0", + "commitHash": "051478957371ee0084a7c0913941d2a8c4757bb9", + "commitDate": "2024-07-21", "channel": "Stable", - "short": "rustc 1.80.1 (3f5fd8dd4 2024-08-06)" + "short": "rustc 1.80.0 (051478957 2024-07-21)" }, "contractCrate": { "name": "basic-features", "version": "0.0.0", - "gitVersion": "v0.52.3-147-g2659d2399" + "gitVersion": "v0.53.0-3-g49a82cb19" }, "framework": { "name": "multiversx-sc", - "version": "0.52.3" + "version": "0.53.0" } }, "name": "BasicFeatures", diff --git a/src/testdata/basic-features.wasm b/src/testdata/basic-features.wasm old mode 100644 new mode 100755 index ad7cf557097c94628e4d6e9f719962df170cf40d..47005bc7fdcf748b5a46871b2178c46191ec9ff7 GIT binary patch literal 66700 zcmdqK37B0~mG^z7d+R2*k{psE2}yukWo$zW3dqy~cAbI{kU?7LZoAv}D~qIn%2c7M z!eA?ec~s^>WfFx5f{Ng*2#5@hI8UfZ0}eQ#IFH}&zxF=Ey;T|Pr+uG(UZQo++2h)4 zuW7Hn_CCST_?j>Xg7AXq?_LuGuZd1O&HjX^y(T=3Kk;cw2>HLBNJ#kMEyy4F4Z>5Y zcWUsOn)0kTDe-Bix>~1Fgl0(!^{57XSrrJ~^QqQ&-5O9gw8!5%$^6Gtm&>6-lj{D3 zw?xk>@7l6{)x2+s0xIsIKb1Gi3(!;fFL+J2HKb?$QzdITh@R+K8xOBNarT;_wL`}Z zuQ+PtxV1y8myV5%js-Pk%sgh~xI@-X95p<_-@Wz-no6AJ60;R6f<*7l@k+H&Dbv>s ztUvbH;jtrzPYN1JnBfz$b?b)Lt_UJ)#-}Yk!M*2Y4jNfIGJbpztIQl<=BSC$F#y!h zuq(aLgS__G#zu}C zVYEk|eDDz6*>F?e9odHsuRU(ULousN9yn!Uc-%p-$f1FFPt^PLjoCDd0atBeK<)Nt`>uAFo?{Qr_a>-kF zNSvGZ+mYELkUjYxwf-1KzWMAz9V2m!-kKY z2&O9`^)-(=pl!xyEnhu6wDy(56YIy;9yl~H6a*Du#3B`a_rj{Pyn540MYvNADot)>9cI44fugd5W=PbmL7 zHZn2HP#yQqTz>rU@>NHS9yxT1v9yti7Z;A49j7@;?`fxWpH-#v?3i)aum|dhVc$_c zNr#VmIqnN&2xuE;!_DF=+UMw1=myIc>kh0QUB2q*ku}KPAqH#oKO;{+VpM{#J?)jF z>uJ6{ZRxtv<;T0U?MmrqO}}zz?TXPgM-30JaKqh}wvQYeK5?pk1y7Mz>ANkx(tlfe z1%~ZQ7>edP;ZWV9jvpFZ@rq+}(lElGSJpjh0`7R_=;%bLUgnm$2aK*5K74p$2o@T0 z)03;FL&gWzk0AKh9<+Y#@`;hrwT@V3_`3AE`<{C_vdSgRp~K6U53QP%{K}Da!)sRT zw%4xhrT=AkY~ISc5|7zqH>uB ztRJ7yMCCDuKCx5zwjcuaIUgd`kxZRMPBKrYA(nR9JIM@~F+*yLEd(5nrt zSb@abPGYUGVs!m6t0e%t_1YX6f5lpplcYHPmLqRD+IdVOog5RX`Gg&1IRiuE!#>vr zkR$l;p|SDfF^mpyVswFT&L+7AdbvII&uiiOYRw#JMaJa1`Ft!J#%6$o z7=7MsZl0j296`E?EsP|mz}n=!vp<$O8^vU`>7E6`(+tPohm1Sh&T#GxJ}S6GQ_{Kk zWYQcl<~>rZL&M}4%f?SxyIgil&=o&fd!p8e;zlj(LdiwjG@~$y!mfJQMFOdDnDBr9 zznMwb?5=R;!bW3OtZ+@hh7v{qk^n?klu)%EP76tllP+3J7%2rQ zPf;33)MKp~#xtXNX}m)ksYD>6VFx#-9(L624e)zQg zJj+klqlaD}*VhcM867(%3KFc?iQvm|x^8Ig$nt+3nK=G%#0Tq};4AU;!O^uW zGrksY$7*Ta=m-fXQEG6_`iUU~-QdvJalzN)8ROV9gD~;ndP)S}h-a+EUMds588_FB zo@DRJ|5n_czgqdn;#qm!!HLnq^(^nc9dApLatB9G93DGKhKXf-Czi0Veg*ut<10D=9=}Zi&lOe&#JsyiQmNCd6KpA z+c>ShTbbX*%@rdj7Ong~o>h6Z5`T!h^CWBKk8xUkw=#c%3XC5Vc)BrAEjYxU%6(JNf5 z)if)6WrCl<^;QAd0J$oe>s#;4x!P4yW1U&oB;Di3S3rDIl2+fX&o`@#V+rf%Eh8;hIVOufX zlxR8R+rw?cN@;LhOQp9Z^Ob>73Nv=}_V%QwBhPht^Q3gw?=6$kUFRE<1s&~FA$Uh3 zx5ywywOntvCR#5Ij>-!*2s@b@>^9dh*74Yi!O>%j^miuPjEoPmtVOpi$De2G<=Y+V zSY@J+##mGh9xtce9Z6FSPn_&t-<5Rh^|+z&!PRodygS*^pjZ#TP;teecUnLZW5XxW z?|ASY2b$I=gKP5j*qzB7Ksd%N!v>Gd*J1BXQk7diI6O8s=si()CEJ#1EP>>u99%Vg zN^tj789)=fZ>o%OGTvX!7(Nk?3Tk=X$nxL=Nl)2_%dlnR2a|badX6ge8MeiTrp(fG z1RqXjE@#1l&vX#nYti_TWScx?Rk4!%Xqh-xtRp{`^k5dSdW5W)qhb+vPcq-954-ha zCGX?O0-raYuMR7@_a?nQce$+~Az;6RtQ5L0*)a#O%w7R4l@Q#YG=`TS@2r?l_-85p z2lD5a>>hkFFT?YL$#g3}GCuO!;oza9%f0OrJnSFW?-M+dOtaKuMn_i%pGs!f%jh~d zP6?k*x-DVt=*00OYmW;);|mOpjSZbLNcGPqJyrnXn-~%4t;d$4>(3?gCZ$iTU$+{A z@AJuQ%Nl2F!zbfW!PeV7cr-~}>WC!_uU)?;_(HPCQ6M-dQt4!DWC*(1E%;(G$Lhe` zW>gKXw}E~sS>&1;U1L_{_#ktLPkoR<$mjdz>L5-WULJhKC&;=nA@)^2?A+Y>n(wH? zru%w6c((v8xceI&#X+ZB9N$dl8(hBC!Mrtr@GXy&JkM|yJmwq9Q+-37*F5+|nLiLbm&_SmyZRKbsq(c~@XKU@C9fA> z4lQ>pv?BLc$;`3g6V^jwgZvDw9uIz<#=rnVG*L=$8ZcAFvI5f#o>*2gl9J|Hm>Fl``m5a`2~QdR}(; zgy7Ff?qbTh(k;9GLfv+5Wjw)#TJD@1gc%K((`&h}(vsjnNru67>(R(()N-d`TOJLa z*KD6|6=`rzEq5?k^N7F|!MU|!JSr(gI5e_$JUFjRXEB95 zT?dNLjpx_WTv6k0Tmh0!3NEN^lc%{wuM#h;ZI>rnK{tKge7UGLeKorV#%$xk8)~e* z^_GjtH`cm~1dtP4Tx;07R@s;Ms!k+d>T{&6E~`PTYDNm?a{nSs1y}eEq<$>n%9_k1 zlQV-w;#D;>CD)E=L9hL$vEb_39D5kdhhm~8xW>aEirzhVlkaNH(7N4%H_ApD9D@g7a{9y z`HS%L_S$scj1Y8l{$>cdCGWJr#>Tu4H^_J7#chjZv zVZMdiYx6o%i`l-Twrxk2C=narUA2Wdq02+D0v*5(_}HKX`N)dk-L=J=$sWv=ckrHC zYm?=CMsR0s`zgy5{P5nI921haqK*<4aob(B=TDjE;M8&sjv8k7?Cx6clm#7sGXEOZ z`)Yr;g+e|%cz;d1MTd=yPaJND2|iGpy}Eci5{WV}w0u?Y!P=bm)Pu(0^5Da5g-6%9 z)Q{A*sT3YvXDxiRHmgi@=MbP_9W!vs!7TQIPfnSo zGQoqj?i1DzV{bZg3m&S?t-hP69y~^veYi7cbe;4O$bFyo zlg1qnK2zJKlH~20&$cCcQuB?}4(9&KyEYlAP;cCHjd!;hi5 z#|BpnA3G!?Z2dZIP(!)cQnr=)#Tq+OMUO>!2Txut_)?7x-i}i8eXJQ>!OAE2a_xDO zvJ3ujZd$GXzT!qFv7jX(RmArvj-brHTI*E{9a$Yve66t0# znGU{N`}n{AIwBpaGZ)X89MM@PJXV!82yQNpbqi59@cBWOU zoL%2>WPBXeh+FTt5pAaB5=q`M)DMb3rw8OIPYRwi6jHwW%9F3zw_e?vL$@gclB zZpMms-dNu;M~i@+s>sFl*^tFS9xq3`z^>qu`l3yZRSwhO(z?{Rl-t^4VI79-dd3dC ztS(z1&vG7(;PSd;S9_v~wsBU}!4++-VI~f;q$aiuZ_1VRR;6M!tE_%iT?R?sxDXdy z4Qk6I(Y;4qa7|r`UWJv~N#Vhp>T<6iF{(fuqm&#YZ?11EC+pz)wIf(ar6nD_rQXw- zrlx~y>x(9(kDr79_ZsB7De8%*udk<_^@1Bzc61EyC(9CH+YKSOvA%Gs^jx96wZ8q7 z*#+C&w2As2#J6cGEk+o-Wo-R&{7@$ZZ?AW2oA}t(>@u(4ZLi?w`mD91wsUMrBP88Y zDWdm{^=)(Vz$CDc!NK9u>S-;iiP0m@p)^Z-6j4%uKP_0Mm4QZwUt>->fpINe`x%_XgH_McL z;L!@t;%sSq$Hj7Ur2)4&va!;@$Ubmz*?_z?ZSOR&QToF3$`poF@N8|0$dTQN9GM*S zv3u|&5ir`;>F}Trv!ohhBgO6EI2Y1`$=r@@acNkEQCyJB>&P*GrLrze=5}n4=anu> z=5^#0mEMrdMJiTzee+6hOy+gu6qPPc7H(n-+hDvZSupiBw&h;!Do?SMZQ0ilLNIw7 z+On=w)}*ay%ep?9mB$0+@PCVtBp)$U0RC9KO;xBnWZzPs*_OBug1$}mYon_(3F7EX zG7ZtzzN5L0_945DnxeeUBx`@0wxB2*=r`h4?x}P3X3l-ye=b6JBHqUMsVJz#Z^qLH z2es8Yh-ngs&B4LrhY^C>>4>B0XPqBEQJWqp+AEutjyAUm!_(3e`l43Q3ez1Iw4&x* zml6&HY1q=gAdOmK^95maTK2%zjU`dg7iQtAJ}MlX^ed?q>=gyAU{*6-A2et1l+Dd9 z-q1)tUvG|6>v=(zuIh^x2Q;8wt_b_W^pa>suz;8Dtm~kpzZGS{UP04-qDCvET$D9d z_QhFOx-ndkg)N@4H(b)-{o!y%xS*Lu&E8l*?-eEd#DXBo(v^#Y=0Z|i@u5k-z$uGF z6lvCj^s8~uoax8N*h0oOORq_vhFSxtW|J`hTRjV2UM>3CGV2Xd8aB7lP@`7>ewd!L zfUcVV5ypPF@nTw75+y`Fo(AxZm3j;M68^NJ^!0*pNz^UiebTADQ5M#+pxFx;Er8Xq z7#(AC!#bC+Of}wG)u+betb1i&0;+y~ZX*M8>9-@$dU@C)E&YDf8(9hWoLFc2lcpaJ zfj8bISj60Y{iz0ckAmzmJ-jq9bS189p^GM3V02cKZf=HPW~OI?!`T}4AL+GD>)(#j zJEP|6FxiT6Ct0$xujVkY0lc@Pz-OEuZV3_RmxxF}L?T4gXf5)nh#e|^5Vc5ks8AXA z91Gvw`r}cNYz7rjm1ep95OTo>6fBr>Jq;N!WO1yhCy?#C8VQ$G}VgJ zo(0euxL#l74WJi|q_2zm)#H+=k%b3AIU zMNOK;$Os7bJO^O@o^}A@lcGyO^FY)aR|wA!S%lZD1>&Gakin0LiY|tcb}eXjtA!xF zAWRzz9P6B5th2jV=Z!=RbC}T4DLgOy03@A-K+y*Y2E9<|PEpcZ2QW>`lBh?c;HN~(hv={6U`+JXTF z`8kkfK@H+f=#_+DL|G^VX+#uB-x#JBhbF*~h(S7sd1~&W`iLp`DG-aOKj=aIfg?np zXI0}PYbd=mq{3xhsyNi_B-H#<`+S4KZUo%?3Ak*-pW_p<#>5zpjT7ySr}-z2jj1O< z1y5O$o(C~d>pMy?N$26dq8;u%1-G)8A_vJdH$DX4w)RB*X7=crjqDR=Hqy`4o7*`A zjrOYfcYBd)B(S~S(FzdCN$H(rS0z*Pd6Jv&4iQ6f`UjwDC6mkSP__WYOlo3Y7@nH; zX2+y`=}**rm|h<)NS{{%Iy?ITfPTvXD)6&oTIwohj>2@B5Zf&@cc#c_(h;R zJpsf&p%%Yta(rEVNQ;l7Mpf5mh zuFw9$e_gF$;jE_8vk>8#txwZzee?b>nqF`~h}P>*ZvjPUx^*XHsgxbhy@)2}u(zIt zy>Z-c6WZ6+svSBbYSjmVy<;$;9|xUyppQA1E~FlOu}Dn9<04opeMi_k&BkzR*eg*j zK|+qwr?NhdaXDR?0ETtJ|+Z-|@l!!4!?T8$(;7&@c zN;it!onl8c^g?<@^-usj6O=p?6g)!?Jkz{8jKWiM7))0hDsvcW!e-K{35KK{1}W-H zOkGqf5aSgTc)sTMg<_S@V=>K4--UTXyWLr98MJF@*%joZ={r#f^md`w zsF-P>3p@qVISEGMh7HLfHdUHLB{Mx)B#~tj=?9^-Psq%Ch<>-R_G~g`-1LT;%q(0f z`U*8^95ToS9aQQC9XU)|dJ=7#Q1ce4(>hqB&frid>3glO^dn*OAQ-Z+D~%I2-@K!A zFGsxMX%rCU&-7||&Kito7KOlM8jX6rkGaZ)+ z2k5CL+z{|v%&NiMHmCmw(dP_q6AkY{1TAGYp7~CA(3qLtl}<600i~9yGqr>`{D8eT zZ4S||;t6jUnqJsgQj8h)OzqSMqbyoV zIGa{X@Or14v8}5nW%`B?8&c@_SO_5Q@g6Yq*BFst&lp%h6-Fd&R(dZc_d%?>sCiUD z+|JNd^P$Zsn~#k2SpX946we1Ed125K=7eoa4JsrneerwD0?_iY*AX@bEKMVA z+`1&%CFt4Ix&%#t1QHdRfR+HRl>(T@Tmb8_6u`BL0IpR8aIFx)H79_j{vvPWJ8&yA z#3}WDkPL~L3}n>lJ0elHUi)}OKexhHICTb+lI)LXYk}>pPEb-r146qEz*zd4%YuH z!@DD7lk{Y7jEq8lG?zAq94Dbl949%HF=axO7G&o}^uzj#VX0-L#n$9&rO z(B=U=9rfq`m^abPC&KJ6;R$`oOb&TQG7Cw}?qcaW!>h3zH}`~hNJI1{OnQkiQrlhz zTIZZav4~=*k^-@=a@?DchGn2XT7JuT5OM)>YF|W+B^|hP23WBG2f|WvU+m4z4!4Np z7X+L|PIS9RC%P^5LCLf#EJEuh=eZO+6=5Fd0MSbYpzBM3u>Ro{+!HJw1rQzz6R4jE zp)X~W?_#RzJ0e(!;*a-Jgqs8|bVL|vB0+gNK|efdJvoL&XkAv|W3g9Q8)hs^7Kjv_v7tF9t&kZo<@;gGLV3@av4uMX|j$ieO;W=^&O*gL5eF124Myiz z8sxCj=#7x6nt-Y+gXT=P$a`%@>mBL#%_g44TDi>7yrEOHc#ryRzy@*1B34RJYB@3h zX}X{$!H0c=zitZ)AdF0?Ch>Mr%>2`uU@G0}YL#Le!{*DYWJGVO3PH*B>z+hf?#UAr zxoMHs!xKCeipX9|Q&GSBXS0hYn#mOc@bW;AZtr*zg)d(8UHGbbR@9#53A>|E++N7K z22X~mbO*5dV^c&XOfJXav#Z3+wn4^Zk;!hu!i>^caKyqUyB8+{T9}<^E3B5o%9^0r zm$&yUb+U`jYGmi0)nFBpB^gS5Vft*?#NA{bE5s|xPpeB1dS}=?%#pbxN{B(EEIJcu zQtd|VMSp)bjw8Ai=N>nEkC z<^eaU;7eC#Ep!_)!4?e0RjJqu&+lGQiy5Df<48COL@6mAoZ^LM3|q?4f+ba%$c?Kz z$Zljni_+h*4xH(xfhkST;B_9Qz~LW-n5yF1ryv&_k)UOP>R4@hDv7%_cjjw z;fVrFPx@2H=K@dpPJ5=mV)0|PkZ+N)tcarYag$)6Ks!U!#ijcV@ek?Z;0|pIDQsTh zQu8vux=!COqBNv701gHkHO~_`)ZRlwIxB#o=w_98P0D2f&XL@n)pnmumh4lkLKnjF z;6W@OmySp$Nr60;BU1X));)jEP|CNL=xwlA8L~ccGFGG2khR$KD?^kyYbdue1iMUO zu`=}QLILGWcL0|F_F{KOjYI6pPHwtHHm9;RU(r&Tqs20;XMrrw5J)fZ+ug`%ikqAk z;*q~QP~b>mM~W$At(}blA$78`^rA?bNpdbs_rXpLmI;XmFSJdIf#4|Y*bC2*I}{bh zxR1h!kT*YF%z{sUp?Llx_V|my<8M8EzT{Y#{+-Cv1RCt0Z)|y&hu@lmM1$t5nH2Ny zU3-qC!{gyW=F`j0PjK;)up#DG6Ps;_md%J4WH+AEz?90~&QI9P-f>Q&$%fJkdZQ_9 zA-B=jz0?-51{JtzreexGPykCCHB6pht~~D0oKr1!Rg}JvlB@~FgxxTcZ$9kuCYAgH z+L!5y`=fqa6F2&5w$UKqTXiwlv{qM}8JIW&OBzdix{N^@Ku!Y48M`ko?LNr6Z7Yp= zy(ZF%v(}40*I9rbrA9@GNoa_lRqo2(Mr#@yloag_nokYR{6#F zaptR7$w;vkt-{}7@byAF=JxO=gD5laQWow@tqWS&COiAw#=z~j-~Xv-;KyHn@Sgg> z*buaK$r^#REV4>2>d#HgLmTXS}C-;9Ivob;YcKKfL9-=eh9tXMedNX~C|jXV za`7dSGdD#xfC>@0746R21~X|R{5%u3^Uf}y=k6%1o97Kecgq{dH`UUI*CQtGsp_CTyZ%*ri)Z1fXa%+So*C`GjI^$R;hIlqX4JZp949#waS(7$G=08vPhqVll?%Ps3to z5x$H1`Nt&7S=rE<&Gbl3)?#+a=Ym67JrdSJdpr>6A|ufSx@fPK(Zi$l?BS~$DvVu8 zu!OOvF{`sHX%`_nRgjF>fsF{%rltV!fEQpX%FyWfTXH85LNgnr2rK{>&>0Q=v3%w1Y{?t4g~V0f};UM8Ctm&IUb7P z9}~@lS!pj}kHX&wh2IhNg}KWythgKl?-!P4mBaBIZJ_2nYs%VLxB-nafs$_N89@EF z-V%MDIX-j1H$?xgS!h&T6El;+xy3=1M+C<3hIFbX z&HVxT@VHAHWXGj@F5pjhbN9fAu3s#;%1ID;tZ49kND3F9=3g`;SYHwYW{9L8jvS~K zHRE<;?npmq1)vL7U!Se3Fci--g>ylajw;N?JDF(EBx^{JiIQgr*=nnxm29?JNnTaS z_q-QG<6eP&FB^fceIIVo$n=JGhO=)I%+)396EZVrTk~J?Z6Q{~g&$v*Fl^6H!xoxn zBTz&FU0Oe;_dyUEkx1IDnm-ZAsOgeKccg8$q+#ZhQC}>ch4|CYA>b_Wvj|zk2{aat z=OYDHBFsGCk4BQM5Oupk0Wj+{eMrbg{$Mhhq3*O@#RH4=TL@7geKhKmrgx$hSJ4-YjMLB9R*U9C2wqmf2OROZ*M_)N5YZYH zGlCIprY8e{_!hZ64!1qqlGHYw)T}FTge`2Ls79{1h#0Any(n>UlP24MQqU@XP9yE2 zB)i*7%#2DO0}Bl0RM82)-vLedgZj+mmUSGil9xst(-Jydq+V|bf~w3Kn^VS_U0Tq= zrD0agxDbtFQRxI)zI9V-itKynzD-f}{m+N&&}5-QssXa}Y;(pAs}sMVjM>b5GeJ<-m3oHm{ACg69WQ59sS{_bmF$pJD6b8Tn;0uawgmpqtr_Y?IU8?l(Cd<#M`82iiaKG07S>13foHEi=q~ zw6YM6&};^b2YXj?wJ(4@{XP;!C378acw&{L>O7$@^rX3YKe%R0-fmEgfK6kr+Q2FD zL&8C*-ej10^94|mD+C3d&Q?c-L;X;-PB#CMlj0_84QAwu=y*!K>sNpHzzW$cIRwOL z4nV?nm;RSkw}l|h7%mBy&9#NAr>!4#!sKQ07G1}rf`?uu8M|%CLs>H8J&^)^wTWq% zx(Xv85fMU-W`E>SvM@5Ox8h|B)2~F$7Y7jDl*|izLCK6k;h+Tv)4t>t0?c;#h;R$j zpF|P^o_{_bDR?q;P*@osOqu>Pg0xk#Vze0P3q-I?ob=Bv{6zC_GF>F$lpM&wW|2tv z1xQwL!pG(RBnc7!oQe1{J&~MOppLL;6+>51x>DU$>X5{$hc##V%beac2SK0heGlCgd9IKwGD#OwQB2$1SZZurUoffkEdu?e_U5o=8*jyzLz^$|&ovwbRxL#$b- zj34DKJ1K9(b!KdOR+=Gw4=XjV6@)*(Pyu<~9jN{Q!30{im*ukXLVz`?Tv$Z+G2I0MG~Kyjd?9g$I7CFypvXVN_Q8HD@ewDld zUPv#Edn0Z7#_4m;4*4U{IqC|sMHxGq3yoN^htF?hA3PtL;~n3+s3u4+ba6Z3LDI!) z?s9NHAWFN=H-mI>8>Hk*7R!oqFhSi_kaTRtDd{Q>{OD~AqBcMN=AYtlx{CL+fegxs zRf<8C4rGarh?GMIToTL9stcP6peGN=wQ;AtZ;-&Th11VCYr}>c@7)mY&AO-VLm@JJ z_37P_Yh9aab;2);mK#N$*U9 z3Abp#yk(cNYg^NfxFKUde%L%s_m1`;n=NM2vprV!%>s|?fopeI)Ce-|O+>*`Pk$CM zy0@qms^f{C-=YdCPP^e~1gy#++b*EC1uWuaChRIv1o$%q$doI2{1zuH`RQ^xlW&0dE?UFTHKFa zERS`O!PXzpg{yjHCE<~kl2@#%3r|iq6P}DgA-jUrfVMJX?ZU_9E@lz~PZ!jV35A;M}*;Lg_ zm}X6y-pvR?mCN8EzDVH~Zm!K53(~j3Ei!%lUavR$B|4FbY-)#ov)gTIN0JfqMJ{ef zJJKk0)=wd1GOL&zv#F%j6^R=e0@w;5JjMhZ9mwEtWVjLHDfEKKP!o4SNG{|nbH{puFkAAKUjY6w2P?y5g~FtFb%4WYp668+hkKCK*XB3@hivltnpa)`rT z`&ne{fw=@X?GixKDahnWlH5IUNKH@rxibW+uC&|uh~K@USJNe!e3iH}`%C~TkA~4| zh;06RIk3s4vvetE-K0udAVR(OcqkOt!E2*8w7plM1ltq`qmHK|tJ$3|PYeo)*9(8o zlPK6LI#SrAzMIrGP#!HnX@f({ZR-&42W{J|8k~J<`|Hqb_86L`$uTPba7&D`GPN#_ zpN971ry<6#Wse6khu>?`T&@M^+ktc3qjiP8LiG z_9RjC+=Gu)f*}-OaJtps8@S(vWNCr=?pCF zuDxS+vaKI={XOABS{CWEs&I}A@*uq(v(I$|T!=<0VK(6Ii6Q_X$py5axomN!fKda) z$zN)EEA)obiLcHDG>Tcq`9F79a&VT=YtiCKW;N2gY^5Ssh-(Tw5(^QOu@I;S9}7W_ zj!6D%G4~GlAXvCBFpn8E-!L!sl$~Ihm-w?03i|Y3!+en5M(>UunoWgnF~ui=4}hsG zjCy@WUzAncEvO&4DkvCiZngGqw2A~ct5ne4wp#NGPxOEd#BkpkF}0a+sXZCT_I~P( zq>l(98zcRo`_uo-BE_PF>DzJqgf{q$sYX)VU5+r57RCB5mJa38-~ek*LQX^hRd40F zp);9%jai77u#G(sSAidfVTX{i)0VjtPdFgm_|mVlR@)vh6ry0_ z`?d(Z$Hp~8BAX~IZ6Y3Xn@EqPO%zsaqOf8U(ZG3qSag_1ebcO+vj|}in5wLifm}kb z`np=t37l5ek(O3t<3WAcc4X{&-bSSzL~MkP_SPw#w5GYVg?*&h?mq=vzF;+RBO-hL zi3cqtuEN(umz`+swWYyg>SH018%S;7IdY+#T3r&2S=R|GcMbxg#Kx7k>;!??7qKNG z`j=qPOuwj@X`41h2gBnEXw{mtNIWy?~n!#)4M~chAK<+XXo_Gg3LDELRct)f#5r8eHhBH#VFVj3-P2&&!dhH0FmG zqXwQ@#vu*HPtunhN&+N|(URH3TJQ^V=iWez9gdc`qH7~((effBNcL^JvFBEtLZ?@-eAkeJ(Alm~RB4a4g~G%NkiXA+|hzoyWQE@q)`` zJ&v*=pWjJ-e;bv6;T%pVi&td!K;l2A`o(fLu*=0@MpUNn@wmzPvQ_@-?6JBbSN z>38UA2&(&CrRHvG!QrWOX3Aj~I=*U{q!`HHrbsd{#aZ6NEtAuLPujn|v zf3gdmnnd~#$~9B&xKueIWhglRKRL&9pdF1QF?NZ!g}sE?r1N{DEXv-0ksPh}BC7~e zjndZwsbYLZZV*O#RisA_LhS&FmhGwuoP&tbRLiVIxIA-*Td+9qpN2SCg@F{NZ=n&W z7_GohW1)1Ud{{^m?nl`-iyUB|L2qW~08j`(>6KBsUt9KVo!N^pnx^d5`a~Y-G{HtB zzdTzYBJXn%waQ*^3;PTm8apH52!um1OHy99$wwn>rl*kdj|H>3p=vttOP*!{X!;>y zNBi}B;}1OZ@nf}i$wgiC7& zhsCUsu>rr|B4gqY$E`p7%DK^;^HC(pQL^F2e6Th(*_7KW~N7IlnZD&kN( zi<-}`Fi!y8=TpBF-3 zM>w12scb6=D>K5{bUOoE+0MWg+Bsk=+c{tgLZ6IDH@Yq1$icp)QVuc&jh1>PmrkX% z@nVC2mnnp1QKlk;Uu>hO+AbPHQ8K+w+`P4oy~rAS;Uuarn)7Tcj&xvdU0F`107Ypi z9*7B4zAbHgJE-k#8EZQHEFD>G9~_?+F+Sc}Np@mMWN zwPnnNl21HWlupnCGG2>P{v_rB4_+Ly?$e$V#c+C@-F?Eo;Y_PDQ*{)uu5Be_Zzqo>(1PqCs?V+Tz}<i)Vmqef%=HYI$ps@pEcvFWicG^WK}QnAn=LS8C26uqiW- z+Fa(*KGV?Qmr!q%QsPga3+e;OqKU93)~WBs=R+)sww&xM{tQ-0#zH1`e{-( z*ko~b8)o#h^z+_~&UEU@rByGhF%vs|l{G7eGcbAc&o^tB_%}ZuW3_yp2<2Db=K@&q zo_;#Ko%eYfqbu((`ni{I!R^gqP~>puQeb|9%GGeLboK6~-~rm3TeRn{4SFcN<9?D2 zm2KU8KD0FkU2R)bvAc%S&jG;x&EF|b%tMf05e$Ji*3!i@S=1Xw{ZYTILG{N@CCEdl zG%=uqz+DE!!ImItW`XK#G8dkNf3=&EH%HvNj$&= zgqTM3q;@|wAOxwuQt)5~hT}2&5h8*RuHsHXO`e>&5j)|%ptH>%EW)N`e3y5|{2Byb zk|*Epiet;p1W-E+in}39CH6hN-&$oGnobO?7H%$l#3DK%q8DtL^;78M{jL_Yt_P-WZT}@RpE=FN*zR9+ud}3_STw zIw>}x#`YuGK>wqkWMP7eV20gnfE0rF;ax54740G5nKb_FVSCv$hqqm=F3#_|aq&28 z37PXS`#$@}+uD6vKB2sZZC6;=c|M&;7vs}1+n`}~28w~RA$Y0NA1Tph;Xo(XR4(d= z2O67H?N4QgB)78m0bgupq2 z5J?G~+HH!2Bn9=_)z$g!mf~j%t$`lTp`V!REd9e>%oz+;-82+Ri=8KM>{~Gpup?f? za?8ceNje}HI;WZ07aE6KUd zDKqc`(o4PL)Regtp?9bdN%7&vR zb=g88)_SiX7ACebQ!6rHuuGbx$Ua@tEb$^G%{pxbxpK*&yYU6uu&Z^oJ2<~(4Aa98 zfoG|E12smuTym%|7`x<9vyU);AOs?4m1`k3brG+-p@nEwU9O-r5nx5w(q1q#PweF` z!ThP|PV9u)!9Ci&HD|SRd5Nx-grMwtF9=ex4;0b%apDvgyUsi^7tZLnyBxN3jSR!O zgm!$Co-G2Zho?VMiYK;OatlCvEbSJ6&I~z8z&-e(?IY&bZQ5;^Rf>>=-=uZJCZe`1 z7>Eg{LZw@46?(AUDE(=GAB=Jh!W|A_8RIv4E{F=*T1qeDY4ZbtWC`qJ(Zk1cr%ObcPSOf#I66 zyTo&a+i7JYojISm==jevWd%lPqMb#s7Ti6cP@Oye0*i=Kc!N_M@gZwq5{}X8Vj;x8 zF!l^0B-V|qcN*EJ+IkcdN>LjcQ8nL^gwwpiP%B8gL_&EPDzST0$_c=lEGJ}{IXNLc_cN2B_vDro+#8Gf z!A>QUm^=gtDlr!%+^FDVjL0FzNP+|e7J?*JSgnG{P0MeE6e8}ts9cpow6PUZh|~1F ziIYM?L{}+=Fu)~+;#L<@h-FTo=nrXnF|Yy>y(Cf$V&9e|5ypZpumpdPD?GBhR{=I= zzd<#J*!>R;K1I_aLaaMhK%^P#3aQr}ZDmj0>=!vM_WlJjUdQSb|`mAp0pC6Q;zaK&M)T>Q`r{ zMYd@mPnVNtU-yd1Y4vjHN0JR$tuXkD4CYoP$gv7UEzcD z_+;Eiamf*$2sDJ66=$mT%?0Qu_k#h-o0w3sg-{_<+r)v3nKLkCR0v)tn{54L%|2zr z@-cwLwVqDtH!lsj(Wa0?xyq%z>|!p!pP~Kii=)UeqIHGBon6eco?`t;^asWkCK#Gd z=m`?!m4vo}5colMW8MgA-D5>z7g?92Z;c#dE9j*p8{6s zzf%i*lr@%4y%<9xl*8!LK!o`Q8pG!I@369(l^y*9+DWGDx6(?{2B9XRk^i`Zi@=&? zRKP`H*r?JSo&d!&6v*tw#dwukSd9EJ`PTBnh#_fuS0^^1_DYUKu-91Lw z9Dk#h#i^kT>*=eTEx85$OV~0s{8~4+ITO&?W`Wtt?%maAdH2MTp|So?XV&W3j8r+ZEO_4({Oc1C4AAwH}%lqf!I;*^l%(xr$ZFXP`92-M4x;5zKJ1!OUa&3qL zywgF2d$nPAtg!bS5Z=bg`NpQ2ZYyJpjE^=ktL|R89IaSp>1!r+dHjDDtCgPRWDzMz zwipd7(ql+nVe{k|{+o4lOh-q5b0jXGgv7sjmn$Z9Ingn1g-EN2hDlr>IT4%Bm*%S1 zlZR2LRD9r)a74-k7-L!s|@;erO&<9ym3*{gM z{HGRQ-+W{W{m1PWlE=4Ela;~mtpi#1)I-`kotU|P(TGNC(umoUI8M6QHtE%gOXtVC zKsLL7bEi4b71A7}5!{A6%Z>2ALeYVX{5nk@UkX?jD6YNFls!7O?M~LB<`T}%F4nFV zV<9}vwnmRjr?c_(F|6n8ZnAeSv+)-fiy3D;+^f;&v0wOJvU71k&rGkvf58D=N~_uI zN)l&S;$2DD^a{6JhreE^gTc{TSbvrct)odAj%Gyo5{_NPY22gsdYp(zvQbNzZK^ISry@Kg3A>B8a$kqRaY%!DLGwb9t{|brT1DIR^P8^e_d_C*D%*&D>GLT!dOV$XhK!z4 zzYp_koH0}!JF24rY_X`H0eqRA`#7Z@i{R`F`Ad?XJR^wuC6jbcA?;pfA75b+`U>_s z7`PUt*!(;ei&FWHSg~3r?gh&%){$FYLL&ROxX|?1r0SFi+Yo`>5$r^Q)(itdZ=J*5 zINrU#in}Z;H@bSUR;&bU`ls+*%EHHrmxb@$GHxYe>*K2DQu>1dwmj}1Dcv&QUaD-X z-7*~G2(yboej-;hioya5{u?ezd^^J9mRtkI8=G2JUga-9Dd@%9)Xw+vDVBZ!`w@Vx zNvC=<--sns6uNzt4Fqh#XQ0&*_8G4IOj{4#E7Rj<$oTwz=x5Je0Fix-j^>zJd28u9 zZL>zFhKpusO%9MgK@@QzCl^eyynDBqhGr*688e-}$+5aWMAhAt3NYejyEXB-e99C( zp;=Arl4*hIkXp*%H%>v<%}Bn3o}faO&G+w zQJYcl{Z?@9I+<=JLH)!QNb8Xa;l!!LHn&?M5iJ72NkkLU%K1ptCMffmBa`+RGrC6d zB-J&m&}@?L5CAiSQ=OGIE&Y$AH%Nb{V!^B{Oa2wfT_nF(A**HbWgM7@Nq%?4{$knq zvy`rpe5cNp*$1ntQ*RAk>}q1cUQz5F=Nrd?HjeRvgnke9OhR|Gi`*!utL*Da8cOKv zcKY(+9@c^LHji@si3Wa8d3BNF5S3x>}A=}a4!2Umyu+Xs9W zh~UJEN4k-YRmju1I^^`~hvP~-AOZTbXWX2D23ofB8*H@5(;}?o|HS+y5{j=+RnU-Q z>if|h2taUR*EBoqkygpJuTCN5OxRY^smAhP67p;#M`1-%*mVdX-ChC*nx~20+}`^X zh0Rz?0%Iq^H+F9qw1bHZbiL&c_UCB(@R)|+Kv5bjc zST*1L&d$AI_SU?PT@UEVSFvK{8l#PkEeeXuVMQa}t}7%%Z;*L@CLdW~0dzi>5YL>a z&ks9lTA;pXZEAAWkq5w=a4x{;-jMb^OSiZYo@)|zD91$f0NGz*GzfxLaJ#NiBKy=* za?K~-3ko`MwJX_k$@bEKY-;NosiL;RjEI<`jAwK~Kp}79c|h-Cg(b_7VxI3EBHmjn zY$Nyf*q0*SK?la$q8*%%#ES-^9URkr5ORhtMP;m~{N9`)N%SO_2M#t$9a)RrcGIO)_PS9+_hoCBMXzV>pi6-g?CTnE%j z>aFmWyq*PvsS?}~rI6!NTqD%*X4IzGOWx8=@))h`<#^h!65QS?9A+zX(M~6`wFs1& zM5Q9bJE7ufqGDxtFww*0NnuW6BD{P-_F-~oT20umuZ@6nF76G|PsKESZj4UcGYW~y zDye*Op+0@dB6}7O#4HQDRm{)+R;#K@J(JHD?O{x*2b2%p*j$PlzpWgb9>6Mlv@ixT=xv!GS(LznQ$Mv z)nW@5=Xvw!{A6z7E-nVeINYF6M6n-M9@l^)Urx+0Jssv^hYl*NjCG$af^#lAFYd6f zF~jMoYZ)>B3I;&%u72wXu9cmTJ{;>h#sGNiGNRu=#CG+D?Hh8VpVHF{DT#*zeh>DH zTKyE+r~!52d~f~c9Ic^qFy4qt1taftv8Hl2f^g-JT`G--)Gcp>LzcS zV=n07!br>1l{|_;;u1mU2})nm66)ezNacgufV;(ug#es`!Go~~KUhqtIYJdI0>QAWAN;o-mE^6-{T zIqeMHX5Qw+Y#5Go>bH~seAKp z{&n47Wc136P8!B9BDa&}{gSy**Pd07{Tkm(mln7Zv46^6w10)&^{`m-B2T-zUr6zn z{F&bt3zzO4w+yp}PrDeEFI`*der{Nx3Eed-F0i34aSKlcc(Qj+Ys|}YQG{N#DtIyb z!(0~JJxXo_c~#R25mz+lAC9o4)3GrPVue^<+Bsw0r%7BDjtass70YV+R-(lc|0iM z0RETZeCHhJJ2UjH!TGBw*Mai`y(ALOLH++eoFDAizF;%2yh;jZo=c)Ou0u#d<0e3# zHKWZK&u>C+RYOxs4{PN_=OihVkGE(_aA3Hfz;N=ziZh6`$r(^@lsI~$=<`YSbHLTk)zP;Gs;h7w&hq03|?iZ;jR5JdpPT90B7>vF=4bkq34 z8D%3Zu`gBYyK;a2XABQ%6fPb5a}Nsyuq&q@<@2^S}6sZHl=zg zF42T)Swcndr~nR)lQ)HuFE*WT;l(IXr}K9P0hAyP8E`xVrXCG4gTQW~dZ`oZg{9-I ziwEfpgPRT*2lr^v5HIqmz0Z*v!1uNpm4(?5qz_Vr&v7hh9@s(5US|O0gIF(4hiMC3 zE%(^h3JN3fnJpL?6w{N$qPMAV%`#BVEs4vF}C=B6M z-Em9S66*(CXhykG34~}r!}{yS$z0ALfaxMFJ5OPR`!2saD`-eq&WTkQn7U5wTC|rz!mrpoEbiy+<#Xfu0!e$gz zc!Wy^_jz5iuaWg>s1M+`voYTr`Bg>iMx083oa_DUoszN`GL^FH!KMVX%}0()?y*(4 zEuU8v6bO|h{gh!fb>|m)`jYe!R!0yJfsVN$E@DssN~kIDr+bVF&+c?*PBpC?0mSSy zae+P-=a9*1DH&J-1a$TqF6TQttH(?*Wn)Y6Wf%$sE+>)IqH2%WRbhb>7WZ}H!!P6u ze2BVoeDvb$%dYguu}A@Kpz<)mE+tGY2NM@sM*U(Gq&>`OTWZ3QMagkg#v@X|Cg2C2 zwP70ciHAO4EXr?Fg&h~4z29xgsYDL3A8y4*6p=NeUsIlvej0CkFw0$8w!cO{B z2bl)h*5K7a_CCKwSK8oJQPYyBc|`D-sDbly`G%-LSW46&akvA_P|XdVE~a0=Imv@` zYL`41zXE)Z$)bm@xD1}WBh4Uow~ICM3yWEX*>%$U&@w1V3P?E3av+e%{^fR5leyMM z_5fzKlkTp;CDE&TYij>!q6>slbh@r7wV#?)s*9a9wSFWG%1$?}`na>7vn#lSv1!U^ zVC$rkP$#LxA6+;6iK=MYO2L*l(IMEr={1Bdcym7XsYDx!zL-6PkKpJ54u@vZy9_1% zEH1I^&JZo}`i(r)4ZFQ#1-?xC{ zn!vKyBs>4&9Jv>JZYc!dR1ANMIb)?xVE96}IM#84);Tgm(AkBJl`M@}H{tprAUhrCfvS(@egz$BXT#+^PQ?#5#VKQ>P;1~7AgT4DfcAdZtm z$y**CvEK5@Ef4Mt)F}Jx`qT*Qd&{HSPf)*Oeipx4){Hd0c3lbJ&UTW~ zbepE!(~vDo?(*Tr#bk9-PmH=KCM$t@a@|2gx~|ZTR9_s2ZsS6i*3v%`ej(u51Cnj~ zThX{!%YhvC!OC0 zbV2g9EY8k?cPtt}{t+0Kj)fLnhPgGMdv8s+wxnL*2qOv3dn=lfnaa4ki@Ft#+`Jkr~0)Mxetx?{%rCDklu#|c+ zV8kgCk2}Z4s2O7xN}H#Is+=d|a*b3vT(Ee6&CIk3ST?mQjAhOJ;LBU|veRwwo&$<@ zYyo9Fv^9zW9#x}xZRqB&Rt%LUIuyfy4}j0NYo_)f2QLMDp8~wwFVd3C<~yI+ROagu z9VT~sZLy#aWLgQHU6?rzRqQV5N*}& zk_6|7eC0~6LPlFqOmCR|lr6hO#C^E$^8qkBcmFmehoql?FvVP)i+A(LGujWUjcRwg z`P2b=q!1Hn-yyK+P2W#Nw^Zu9f3cHxz=mi?JyoR7*F;jryPFUmMX=e_^}ppcnKf}} z$NJctO2tl{7HdU&>KOef=6rx}BR~`1sJ8W@O}dHP2=Gl*i@&U+xJGXsus+ZGS3knB zlVabjR=-ErgG8s|QcBq!u#iw^IuQHR8!(~V@6p2AOz#V|WK7rg(RzA~`&e!I7JVY6 z&mwoA*@MJgDxe+%@}s`S%k&UQ>815>rL>(Yzh7XdgBoXMIZgBPheN;nil#? z(revAoW9+sOaY9f1;9LMcS2P6ZQv zVPpc>eR?vB*KMt+BUu8w_7Mow;+YQ_Gz@f2s)6o=5}>OpoOY9aCq)Qtl_`p2Bis4U zrUGwu1!Ha^OXwUSI*iez=nsz`!{wQN0m@J&Iw6U-V)pn(3m+6yV#G{h`a7O~X}*cw z*+*2;h&I!yNC(utA?Bw3-4D;pQtZ;Mwu zG0t={M6D~Ygr{EDw$0zxDEHauzzkC?70Xx0?9Ln3Eh5LV11xfFmSG}fJ7(M@#K*>D z-QLGS;+BgEE$6-Hob%#zUv&P}@;s;5ncltnW=M&;)xynnWm_FAquf|v+SD#8a7+hn z+Q-Cnz}>t-@?{xrb?DQ7Rm>Q0ij^QAn?$feYx>v%x9tEROEK{JOW{(>Zm7FhJ>t~h z%UnwsGgM;Vy5dO}p~MeU@mh0)szJcAZ;by*%4-7LEa)1bXvmN5D zxR~q5!B|%3j0`_LT!xaqF-DotoNnNk#4?j4y9UYFa#B9Mm>cGUUdh{NWp9^LJ@!!; zP?$Zg?|+{_-e2QKLPr9E(-XD|tqwJ$++cA!xNJK^HPnpU74g<^6Cu{+v;vt(rU^Wuh zTH!KgH2t1(&FsBtk|)Fqpr_#vT}NZWKy0SbItpM-sAgGy3mcFIcRvA-S|)W5tndTf z#Z&uO!h8}Awx?Yg9nLfg?PFiiaU?HX#MBg>F0o5}#8uxDi018R?%N@*+6O7GgW%zF zwj!|sP0H1(Y)0DN4nc)@^^{H}5jS*8X}Tt_{t z1^UAUd5=!Hoe9-%YgF%X{EaZ0=Inx9P0Cwy=`Ds+O}k?==0kfWv?ZslZfnTk7g04d zFBQ*@2aa!yqE^?T=-JqPqlW>Dx1H9Nvw)?;MvelSRNWa-p{twv8tFSk5;eY>b!%uJ zZt{+9yw>kvP8KsJep>wSDJMRQhse6eY%*wGE~vIq?OM3|y)$7ldclkPcuF^-z{H(5 zMH_tXiw0q!CZV_aG@Tc@0GU#Gl#;eyXpJ$Hh6&m(H#{e|<>rwT3K1#}!Hgmo`n>gST?U} z7hgt+O)TdJ_CcwZsi>cBet=|&!!b{b{i+oYq2nsZN3!|24uUQ5APQvvZ!GFpOXy9> z+5epMZ5|GyKt=A`JjMeUt5bZG=iePu6)M0hYcDylGx%7A-Oe`~$Rj%uD0wpG#*vE? zo4@pCW}qTd-s~6lg~jRpl2eEjL<}9p)=?i#$NPpYe!!bF4!bb)hl$$9*uB?yi79CI zpVg@ZYnX1-(^S@LyD%|t+#&g9dgs8|vUB!?&^&3NLg_o&M|nCCyZ2<=s)#bn>l0YEd7(nOfC=o(H%KfMal|GXCGi z{~P>2=KrDPOyg-f+}h%39kblk(!oE@jkczz#{+M-#0dPz)>I3%OUrhvE*1r>B{`Zv zv5^VCSYHAuZ~lRnyzY{J7j)}Q6Jbt=D7`yooP3N26Vh7Jlkpc^tB@FX4c(?LzOo2( zP@<4;M27t&_M^pFV0soFv~N`TW2tAt=u11+@P%fA!~LZiciDDE!Ru2APVa;4n7Hgu zXz$o!3IPD2@o5Goad9UtX6F)a?1G?4)5ggUHOrYZVAo>XKkrjLS7Ax|D6 zCpDpMY<-b9lrC9Qdd>FhK!Bf`V(&%dYLbFhH52E_U6|9xR&L`8AV*f`S8G33UgqMk z)!8?CRj7M3j+lBHu+zX7vCh|#N{vSd)wiwt?7M3yC38KAY;F+`ACTS3rHy%Ll02vX9+y1q}7|O>XUb@l9lkB%WT6Unl% zs0&kD!X{KwWbWhYNdkX28S;S+5aws)@2+~9s z$v2+^=!0ZCWq*~ME6p)Z8zqSOa@A?{kAFQJd!iG!&!ISbOWQaie0CS}fklRFX2b<6 ze38Yx@NaX%0GU^Ediq4^8>lL=Z*ZqiT28>ZPal+PUu^up3`S!MV61QwwlE33sIH9P z0M>}oKBK-Kw1g&C2!M{2v)FfHmm8gftx!gUn#uP+(#eu7bTXbBg_FmE+Hte_S&^yW zP9Fqt+)P+@F66fr`LIw5ELoYFLBld^nb~j}qdsA5+~#lDv4K4p=5n-HJ9q>xlCG9NnreH&@n zr8{7?9DGa@L{`f4mkN<%my6_HZllfra8yw6c89J5C#?3|Xg+V$ozhpB-G^c3zTK^! z08+-;m!jfiy@{m*s-B6$CtaFXRBUe@K}R$Bc9XU7pHsI{1RkgjZq_aBX6?qci_VW5 z+_~uNRgL_+c=~`}F1P6U6H%L3<&r$x_ASWDBd4?T7V}B3S_jIaah8+GM7c3t_k2Fv z@YDnHF8N%b_>)lg{b}jQs;Giofh(O>E@^G^K@NHKWao`i&<}U(LacOgxtE%#rlf+^)KW04$UJW#LIX}4q2$3d4EHJ zkr8u=P53s0t&E7(A_en=dA`XmE6{RXIV^g)uydt z*1qP-%o^plqcz}a`O_R?tH@=kF62gs^wp~9CFSrLv8?kNj14YZ(JGPQ^+M(~I(;n^ z>xrYTI$LRsl^0P4cTn!1lh@Ikrcr-yfCxi#?JOftvv%cwJIy8~&HmMmIvU=5qhG1H zrKFEd7N*i)$qji$h}yd((!M7eLSA7YmS{fANk{qBS@s?1-ujj2zSgjx^yNF{*9i4x zcK1058PEtRHL>4Ha#6u8d_=4A8;KRl1~&R}c$i?_B{&Rs!4wxnBw91kGhbR(g_gVE zVn6n#e9Egy(I_H;wBJhYv}FtZ7nm(U+iyGik1(qMyGpF2_$V{&YGXlgf5`O6|6keF z{Kk<~@wS~9C$=|sBEcd!lt(^z4_NWU8(G#$%h<6OOL4O6kKNpw>h7AElDn%qRn;?| z$O#}oyMMq+2v`X&D^8Js3uw;aS#}h?UPL=3ds1$%6iiaQ_$g#i`auxXYzEZ2nb!6o7bc#EN+T zJt+SZ@iRP{`|8*uE;Tia8$jHrVTpj3H@;7QGp$c&pW&_@x9eDJyn%CkGwl~1Z(MtJ z(YT7KOfDMN%a=l5V%Oh)j*{1&p~&3HbL24;_~-{07wBr-V8S&#ySTuk-uWNTAbOmk zzqR}`IBP86RpdhJ1^%;5E6XAR6W(#l3Jo*J$#X{>3$i?Ni8@Jf4owq-1EVKEY?=1asmJ;8k z-M1Pyk$wa$FW^lVySek-kN@#E%;$CeqHz;fZ-2;cvE{15H>tu_V|iEs;9ssQTzd-Q zl!Xcnhw2);plsOiH-%syp{ z&)HQdW|`f33JI2v(U{=WqR`Ey9?ca{PT=txz4}~0=X*5xfSKmfBS*b8bj9?B7!QK! z(LC7-w1>!S={JBbtwHX0S1_{&l6{Eq=~Nl2B3hhX4d_)3rg6Y~7R3Ko@eK)H8P8H= zH%l=&c6j1NZ;f#_hZz-dSfF8fmiz<7@4h%*nImJ3cCcVy91$g5GTN;IFF)}lUN1>} zeSQTWHREwKIlQ0x99+{K%qPU%N06Jtw>DlrY7)5$4Hu4&!RdW(wVE}i41nOJZ1cbT z%GCh#Ud_$_cIrf*3Dt7W$f$6_Dy{Yl)sayue9j@yb77>)3&)(+EY-+VJ)X*hVPA+u zq%snQJAwS1B_h^|V8R$}E@cVz$}A}EGt`SgD zo2OPdQ-GD)H$pt2lMiH0hj)oPjzk6rB8|gtm#dC=*jK{f>NM{d4kdF14#%hl?HuV; zSWq3i+<)*68h5;Za=3TWGNL1)W7AT}v2d-ltNAGyxiNfj$QAF194p%R2w98!^0q`K3*l(hHG{d<6w0T;sjCF$#&aSPdI>ls;QvaVomp-k4L6IpqY zN_A$}y>YHf)rN8u<0-VC3>dVuj%#0Vy&?=n0-knQrdAh-dvOm)QLN8LB}hdg@&17w z+#6N|@q@}WIPxmUtFJ3tLjNNDuz)hW`k6M=q%xUEwWYBx0ZL&lp)V4~(d4yM(uKCj z^K^i`Q+1|vUsV)j(ki`@Z!Q^EZbVP&!ltF{q?z?t7o-v2gU}f|L&ajZOk}zzOgq*6 zb#~H~mSs9AQbDH5x*FyPY6%n}SQ|~x__@rAj1^nk+lQ@3ERk(E9C9_a{C2!OV@d!B zSi&8@q-sir7nSj)^cn)=-j%HZD%#pvH&jO1)@DI85`v<0UZZ(4^f) z)@x?HjR_l!pti(Hox6~VyjKWdRb;svtdRPJ(nY6BG9)%GOizT7gw#(&7B#bI!=^G8 zAq>JgZXB=>=Gsac5|jza0buZe1Atf5D)HC^5Qr|kGRau!B}Chm=!agw${tO7d`9f5ly3;h~SW|xDC)8ZVSliD!yJ2HV#yk~`ag08Vm zyOCoof`Ad6o0o~9K*x1^+|W2?W1jOEg9X$Y3k>QgOGEyoOGVG{)HfU`??H7TFAq_9 zfPV`KexoZ7X7Z>Zp@-TSqR=S8_ZTHn|A?~CFby6K2%`9ddaXPI(Cf6)VU}t_m=HD# zQfwqUfRUp^fg|+jbQq^VHhh0^cQevD1*r{eO>B1_6Jjcod-snXKDdW+upK+$cZsj|n!SY$N^+_)qm@ zypqQle@;dP*44Ks9~q!6%25X;COG*>V^GFsY$TVK*gMDI z1G|>N(H+KQuY~L5BSY4(0|@+hgF#Q0TGG~oc`|xJ9~|>!{Lh3>b`4bqhBnMz+dtHQ z%)qL{2ag%*U$fuH$nc0Ud@p@rG1RVL(fGFoU>Vac?$ zz3Y!YdSK*`ffG!`g6Xy(TeogtXqAbj8(X&D@%rrQ>_0d(IC`w9vd$b^=kT%N5dg?P z16q5L1$n;{2Ue~h8+gmmz5}CU`wS0_4V*Y;+H_2v)IR-d)(qJBEU>K(7#MrY$lx)9 znC%fK9@vlJWV)#x4zCX$7&>OmLNPNh?mcO2U{oPktY|>p{SM#vi2Y6+8yG=%z5Q$Y zhgJ@lb_HKSc*_Yx10%rK$iV2Rjx^ghCU|e_U$cHdUo6fceDnG>V}tCwp~?=v?OTV4 z`hiv1$T92J4h)SM!T#(VT>nBa?p`PrbSfPJ=P?6ghpb=Av5pw&9~uSeMwM_ojIITI zAU>u^?kJ2kjVh$t2M-)QW~Q?s zwJi_dr$q6YE7uJ44;?lzwti%2-~O?FGlP{aFcg%Te4aP3X3gNbvB8!5tRLAh0Hh0R zjG9z>#!ynOnA&e26_WOqBLn?o19%iFcUki9GraC3PU!G8gW$(m>|nlqwg1TQ+Fq4x zp{X{UYu_F*-2BeMdFb#7inRF~`qvDu!l*|KA2%>`&?+$B;L(E+GZEWqN~a_C9o(SR z9nOzk)9^e}dIqxf*>-U7@G-k-k2^F$?#VBcb~AIZm*3j%4nKaRK}4)xIe56=Dy3K1 zaSAVpEJsPvcTV*~g+nPmOP*y!P;cfmJ%)x#;`Qk%0}Z#w9%dU197}d13rgc>#u3j$CuhxT7Tv~p~4cu1LLx^0V5cYFOF%2rO(ylG(N%Kqb;iVquHH?Vfqu6rym*M8f; z$l%c@^&Wh<5#8TqJJ#a;R_*rs*YEm|`pUtpuMQtrxej9-x2wK%hDu-Vvwn1pgmP6x z-8db(-_S}-*T39%V1#_y4^23reK|?k$0;&F<~$+j;D$F1h#o5|y|{nXDrmgfRBP?4 zhSwjph6->FACtk+w+xA%M2XrrhrE@r&LdRmcq1y+5$tez)7w8fVBg9F9D?86KQekO z9HV_yjV`p^+2VbM*RG>wA0R-|&iPYs9fH&sTRNoq7tL)QJq{Rv&j1#}>|M9cJ%OcC zhE&3OCrJ%hN#Cpgae(?LqN~OBbP-R}RlW}zRo_l z4rAHqNkc2?x0tz=t<{^WCW@;O7=3PKW;LD}#Fe=nl@R|{=FUKIwG!h0?f;p1;oSLi zEA!^gT@Xb(m?$#QtaxE{?u?G8qf+gtbm0HYT>hwiv}kSz-lMB1jyk4A zv!bYq0yM5xO&mobp2H3dWng!!n1xwnX3eMoBK~hirD8$=5Jq$7hKs{+P6fT9bZ&t9 z_&;Oj;xC{KtJRLVVWsk__|*V6jw_Y8S`A|q;UTVs9dpqO3g^zMMwM!HVRc$n{#OZ5 z8ipn`s1?ucU|pGh9CyTFN8C}FTa82fjw`c>Bn&kd-Go3B%FqP0!w`iSyJCWBbzc0c zh1DGw#%L6qD4dIF&Izk?W3w35cc@h7&)s2%TBV~hKb+HDX`d4l4TOyUm^qk!Jq9#k zHI~@{YeX|#KD>=8X2$<7{#X5*n`K8w$4=npU|kSEL;|qfyvoKP+q9`Y*=Vw-eju0=ItD|WvVR6U$hKCTg zyg%9j(bu}+K@^;TT77HRkM%=w_Vte(V?Gc~ABBI^2c^)r9yQFh(eyR&zw(0XqGa9h z3G&SM*GHZ1QR;s%n(5m1jScs$N4U5pnu{X7?Hk@OFmeJN9eHs>MCBvn^!>-c$gsIF zn&S(OhQq~LeY={QqFJs3E!}mu73SuMp_ew6Qmc;pj*_6$+!A&2Jx7(kw?=fI!h`jd*70IHeWBZeGh_*wp;C@s`n2iI=Yd|2w{!=QrTdRUl!x|{(agf5)c91?>58P6d!n@XEOkB|C2QBO@x5$|W)>c$#%H2VS0ugM8>Pi( zsq@(=Sv9!9_i|q}v+yW2J{NVmBI)JxQCfVKI$wyAk%6_om;0lcg-5CJ#i-L2NiPpX zY4KU=d?}huT=kER!V3D%z8uYKE|vOUiRLuCk**(%=C>%87GI5K6ORoeJQU4qE|vOU zi{>=EkrBQg&2LdGExr-WCXO3M_+~Vxp;Y>LIGRmVHdOvrG^e3dDt|kgO^h{Eek7XH zP%4!lrSOdP!L=7WO4)ZPJmoS0`7ufM0-QjygBuJ(FjvFi7<*J7E}{eFbJ zg^pGLSpaz=nrC}&eDecs#LgODJqeLGwgBP}ArggW8S^RDQIU{=o@Sl=S?c@4*rX1VM9Mf2!0tc&C&-aovFNE zMU21thM86CgE&cA?bq5dyzP-yeZxol^4~@82rw5VgV}jUY(t zJC;en-$e;K9y?JVe;;-7@tFS6zBNqm{2|&=pjZ!iLBmyjHVFcb7#TPL zeQRBO_s3`sKsZXny1t`bT=!Cxvfj$Rfsv6uo1ywsG&e6pNX4vY-*E#cnLoFB0cgx$ zTD=&>i%oH%tAP#3(7-IO8(e8liM#SK^o4|>r^fU1a)&C|Gx!##wR}Z#n9cEwl?Z^4 zgY5(E8HAo5&vGTl`N;H)yl}+FrDw)na0?KPf~`O!9~7PyFRCkVr~N` zB0~dTZ#VOvIMvcYDHs@9zt+4rUaTAl9QaZUGBVf?zS-4W8PAb65N>g*`qs-tuZkCI zSHo+?iyZ9(4Uwbo!z7qPeqV7C8wOUIt8D?j8&P7{*l9a==lyn|2A}Q&Zt@z?dg;D4 zULXM2=6$Xi$GXnW-n|o~nCtBTt`r1dlI4T(BI!sf(TN*|cML>Bx8RT267MLl3;k2c z6duhD@sd_m#+V@O8_YALH#T(D&rlf(gyZxLtX(&DlDWx}6!`ByX4H=|3PRl+&vv{^ zT_kKUYHrD;OOwHUnEzHP0F_Kug`!!biyF7awc?9)Be3((F?~lNTkPuGmWwqSLdlxj z^D$7ODHrphc%JJDDh|xCl6h;6{^59zdowD@6~U+ZNW7q-_$VY@^xYl#+-%;29ep%@ zm22Tj`K%ciR`?k8na*>)OSrK7jV#Qm;7_X}(1;f!24$9SDE+qu$KSC*qm@ z;jn>qNVl82^Y^{xlkuG4p*1I2ljMRd^Qm~D6tAZM_OH}X%)h-So-s0T{CcohAO7^O z88x4d+ecUSNfc+c#dFqL+tw#xizou~nHV`FHF;X>SkMMP_r}Ru{n;2+gN&sj=o%OmM8%HDAli5j8<^t^7eR8Z0-6u#K8;#R#(b^1)BP-v9i z{*}j>Q>t^yrTdRS%FSt|%ERllbaQo9q4MxL>EZP1%)C(Rol%{U7fGcv8;8k`#%=&} zR<$dyC@>8i4Q4lI7t0S51$77l0dr1uL0&&E?>*_DRp#6x$cNi?VRK$}Uf#l$O8s|M z=jU$@0yGF%dQUnKQJp!z)meoi>(^XXDzvP0c@?WRzOiFdiIT3U&M3SB zhnRPlda%~uJ=JA}YOwGlFx?}4s|JqlrxUh*9apx&x$vcA4fefNEM@r-eSL+;H8WRM zu`=9Hi%E{P!>bVDn5(KgHNE!oq4`(_dhgTOQ7tg`LlesVhCvwftE=7Yq2W~n6xURD zY*DF%rT151Yn%EZwm#6Zu^mATz_nF4Agvm!FKVuRd@hRFV*0Du)2GT zcOKc}5@$`u{sSp&++h0)mizIQ*$?0=M)MZ*3B)Y zx)vE*t7)OGipj0jnT0ZuwA)HWL)_nVdubHrWd?`WkD3ovUtMU7TszVV%%u|x;(MA8 zSFv^p7KPh~7!%Q;(A3T7BVhBEZwkoU)!Z?KTDzH#Ld074h3upxVU*>q2W&nD=@_Rz zO&;7ZiHts8-NAY#E#SYix`cK|=WH!jt9jm4eXVza`qeF>acfoe6V;vI(G?@3meuaA z)`-J@$?7y3ciop@`!6>FN&UlD-^Vv#nGep`H0msX9uE z)$gr#X(-x1Ha0SN6!JBgF~<08b$(O1DnN5zb;qVxL~!1N`CN5j%PN`#{Css@(_0nx zFSO_yDY#&CD08b5(eh$oY#0s=99`_u{_^936#eME>rWL9UuPjl?vVNa|OsuWqPflTg0KL)Dtrph%Kq z&=mPcdOM=9p}`F3?jhZX|cs>&nP*f-U{%FL+wju<)` zx1pmUI+(JK3_tKOG1~5xW;D#>qPxn4e%|K0rQYBs_93LkrZi+zzE`cO(8)l>S9$aA zi$mf%C*sT#VkKP>`QD<={6Gvn->|fi!_AXb=Ftxs<_;d=6o-)?R_8LC+P8ja5FS$Q zNt>stU5#bz+B{ud+*Ce#0u;ElAU|r+jxzn@YTDS&Jj1%fBS>>1EFre7B6_yEs8zW$ zXg{gGs^x1hHa{Jwy#?_(l2SGo!*3Z`zY=+<w-dJUG7*YCQAdA>SxXjqnr zrDzaEKQC0_^9$9vjy>QK$X>&~fn(OJGQX%UYI*_f4#S2>WmogdiC!aqtIl!3lBP8K zXg1BSt6dGnYx`GXnff=?u8{%m;6wzC!Jz_H4q}6>`E9j}C?!(3zTomB=66+Q{x#Sx zsP5laksH%&Ls7;20f9wXX}<`>z~C1v{joa78T?}LOVw07h+^5FsxzsViba2}&ZM_p zEc#1zmU`R8!cA>Rx>*Qw`WL2fN}H^WFm4-KHE^OiwQc6ngVYZ>XPst?#N$w_x4CTx zsg-wGsDFA}jq;=}Ny&)IW(A27&S>)~B#V3EdQeihXSTVWnT{@f&evdu_pCObrgBZe zif6aYb^Pf{!KOni%{gtaZhQenJIu5N1sDtd=Qek^X2_h^=Cfq3LvEum?`(6MzdUpf za!h%|oZq%%<9G$=rOE|uK1b%d#?A#czOebtnjv{}Q8U71R|i{N+}2LZ)XRA8lC~MS zNJ?)1`D&7vtbmLbWERPm(lb&l&sXyfbQR=d- zHg2(IEw7)%UV-NIW_k5vzG_-?mRHY2Gu_4?2mkfhdt|o;dB8sy%_=H&MfT6DGfIUk zpy*4wpDS98MPQD`BC!yq{*~t%`Ul%Jv?S*>7D@lJO1qzAqSr<>m#I^K(+Q|eu#?=k zM6*Pmd_@*s7ftKyFYao09KnT9hGp}B{xSmR})h+=_dTom?SmW;&s+0 zlg&(DU`et=5Nu3;$)EMGW@?ys5N-^6GP5+? zb~!q&n{4CiIw~A8-DIe{hZfg@nMryY+F<;H(PAgG1_bGG^g)o6Yo5YY z57S@EAn0c2O8cH6=1>dw2-{INU9HWM%YssGp%!PRyAt+bCQow6dXV1BztfGBohL)L z9WWieWo8eP$RFvJB>y#-S>3$viRVoS1fFC@y^((m!j^dQN??n(lHFvAFcDtiFD zFoz4B<6U|h$1=%lWaL`q%_t6PmGpO7oHxg`OwwV}Yc@;$c?G-!*B0-|d={eyr-b6u z`Do2}J8Ojo$)Yvv zLG55^7}YDJZgFnAaMX^gM@n7{(upxK^e@gNRT7MK1yF73mGnCP)e4pLT0V4#<;MV% z02H14Crv+381@s^h;}iH@hiiBdxYP7nmyycH<(BGvp{PS3tiSM56;B!+4aJ?Gt!SL zk1rNfXDg|C774B-D3n)R)|XcQ$l5BGE_zddRL053>#6o5UBKs3@jplNHiyN z70Zta?iZz}6JC?lrPS&WpbE@!sW3J;9DiBm;sPde5?EGFPhmf*LNGs|x;+a-TaeA$ z>*RG&SNuytK);+RsKNtSNvIP+a|kw1>Of*u_e3VXWu%_csfIwA`zd zuqW)P!C^QI4J!a5`|N2{rqk2+3Q{nM)uJ)@PZvhJQj5VW-Dm(A>yBtyD%sN2*+bGa z@l9*ebSbMX%bR8kq`1c7C4l=X*W+UCaY1*4{%Vw&FnynUDT77n)fTGd&Z$5x@{xE) zBzgJ~)=-Tlmio1}2tE?TRm^7kQRzka!i(lI8fmvj%<(`yOK!t`3cN~TDo$F`qF&q&kR|3_&wOSHV;{{x(@ zNeU%DQ|{c6qARo{?XCR9>Dhd{uofp>B##O_WJd64%7y3J!UG_uS`&KRDFcu>-6B*E zvd{ep9G<>Ydd{xrgBF41$Rg)UKNW#dU)#Fn;V!Vl?E%2Tp3LlGo}lkhr-PEM&;Ek{ zwbjg`nF$<|;0^jRTc2LYX4(4W%&^kYpt=J!@|4K*X2J@)t`6|JvRbnjY5TUbANfxa6O9&PmjFmReUCPY{sBwQ3d|-%SslPGPoq zb-I;^j@gQkgU-wxHj(*(5K zAmsRaD#zb(P56_S*>UMbg1_TvCdngg4FPO8x)vW|0iDyLR#yc04^|8 z^ksxlMF88U1|Z@e8no5`A`TN1@8U18#fmfV4-C|4y-Uy7%%7WHYCElWfs}6A^!u5o z^|qg}nJ?PU!aEk=_X7S#J(e%UZ#2iR6=!YUyjh|bNR~6i7jtItOZJCMWG@OM%(u>+ z=vn97McGBCl1sd|r;=w~zsR|MuQHH?cd^gu26A+%w0Vu=ti7laL}4?4ga{z6g@`N- zA15Cj0;-Bz2L`!=T&#M$6E?9{DTdLIPWWLqjD||Fx+?vIG!m~Pc^igmK{kj5NFL-S zAEIPUKHgZ9Pkk>Y-_9=Q81ZbfC(b7(lJw&oFCTuJ3@_#?`A)%d=WIiM341X7_by-$ z)$}el0eAoOd%^y+=7@X?ZwvG34pb%(|8-Yjy*=l7>>W7WT1|}`^DzXK-3%oeMnkz| z463ts`fDM5IPjtbhL|>dOz{rYb&SBP&YLXYU_lArkmxY|p0LD7m zUBU~q(%V^A0=`NXtfpXAlEcY;Ft{zJl?B})9hM;5ELnnz652m~k{xn_7^mPx>y_x?6gj?SZOnK?0|i^CTV45vV8t7b$RJaFlNGbVIme zOFmMz$j_KKK#wfLS5cLZ#05;2Ql(0u;et~o#0ooA6$}s|&%Pqi%}5KJRfqS;`D8UP zjH{4cEEQ8f4U=pwGhih*RHRRPJ<5oV=q=f+f(-;@>`$@H*gnOEGni|f?Z5HVOpiKr5^5u9zkdr zhW}*A@()B(So&Pf3|tULxPT!OUd8+04}$W&=fVKjE(-KQXhB z!eBHEmVTKP_DjAW2C>u6+e{Hv%T_yV0kp*kynqHspV|vdei{4#{8?U)6$^q!cnkE3 zmJyY(uu=ABQLq-Cum>Pks9-}jCG&$vAptJ#gEq8A0B^(ykN_MR4M7KL+{E>n-6X=6 zD>7Iu_+}U+s#%yo4+TV=0-1)!;B@gw&&BZa&;YdCdqUdz%q$ghv~xwH_-vc;$uSR= zOOaWU9cV;dL0DF+WRp7IqePPs^r=v=8X4)}Ez^-mS`f-sC!o%W2AX&|YZx#Ws4hb2e0&eLh3 zusaYKI5om%bBnMi_6^_@#5l5xR?7Yo96uur16U_po_$18gVP;|B&l{xtiIgFftc_z zNU%N$ftfN@u@K@0Ze+t73KkKV$*IJIn(Aj@mFZWQ=dY0wx*28Ig0D+Ktd!f1j@tK1#1N+~&3m?DH4HLT zaRq;NF=GCMYFouV6WTRbFSzUcm|) zynkm_Woh_!#Qwojvrl&${seDi9$~5Z7fBw#=mLUi==R&?Zr{3Buq=dirN5T%6{PxW*2j9gk zorqP~(cbFewCzFnh6P=F8RT+m?R%JMT9EE#X5d%H9%hynbna#5f~^3O(9fgZWF0J$ z*;rT+`m;zTG4#jYDLL?Th5l=iyAEoZadONJ% zqMF&3nFAMbB?KWQycP{9!`@9B5l{Xnogeg)H3pO6mIN7(Vhh9B6@yT3tQy;zEy=dR zDlrUJ#WIHTnOtBQ!|Yb!1gYNy9IM|1IiTMJkB(Ydo>IO8Aw(Tlfd+BL#czVgK9S#4 z@u;oEuvsxWqSb`b6hmqG1hHV+4FG6)s`Qw|FA*m(8O_}iCU;K%H`~0T#Vd<#n2RkjRIrRvZ%URiM8l$G%!M4hJejM&Tr^m! z)IpPJy|1-95m=w!>7WqEE@k~2{C>Dht^9t5Dmnj7-9rq;=MuJhR=oa7s&1BD3@Uj2 z70<)BQ5hsNfNWuUO?Rm_fEf8O2;4byqDbfvFl5*h_8^P;ZfYJ`A7BnCt7JEb-s#W~ zOZ83`Bh+v&8lCN$Np_d+ptnwyh}5Rx>g?8*v}iO9!jr)>g6u;%g-5LO>Yzz|U4Ce|oo3*d;+TcqyHJz_UNqv~eb(Y>=I`ersf)Xb`&f?$Ud_sK~N4sYJ`={27>v$yk@WCBV9{tTeYd>&gro zg1Mix)`I45z`#JB+AYsc$z)8b@!zNh*W}EgPT?l2pCo-`bsN|6GW(*5>IN%stt44A z5Hzl2Q&k-~uUHrg3@(;SwaE%WQr?SRUm!JR5v_C**zk@;>h^E=eF{c ztN>ZQwNnx%4m0alb%Phl)hsBnISW+(!h;uwtHi>b#^g+_%MM^-^0Fi*O1z9Q8db12 zfRb^Cd*^(lWGA7yUC67MH_GbY1G3^X+pYdB7VOrTKLe5)Ue@q3eUG@~2A&!!JT9$w zaIG6gz~Tz(xHToDEH@eMQ2;>F$sW3-C@vs8zCT(6hW9#aSRFU(gbt@}2+G0@$!hSh zT{z4wNG*^Yp4OuC1MiUDHHzV#s5b^#LUGsX^v@iOywxs?C(tjcZWPmOzSkG(9*f(X ze7xZ6H?J4Uo=`+*b+-gAy}x-npy)=3?Dzh%*vJYp-Ta#F0|obp3oXkz*ir*{=xJb{SL0z5ZM0-j@o>4KCOwCL zlQV-H*ZHL3q8rACh?U&3H^U~o>@5+LU-mYI_|O#ML*wH^bfxhj00Z1yez!Zh3X{04 zprZjupr*}E3*ac-feSHql^&!8xed1#VIu-W4dKw6z(m3mjB=T@DWaT?z@!QmhD)&% zi@8E#P_cx*9J2F-GuJ3NA7mV%PbV ztcsq6hIM!e=^2h+7d zIu#1(RWiv%ktKY}dY~U^?jJ}@{{Uw>e?W#Ve?a1{O!H*9<kFCUt|Pi{xA^0xw~6Bf1)+eNdmULmB(1Wu~0OWm-DJWE@*yO8Q7UhfZeUomWp!%J(C<5e>`BO& z+*%1b9P4r|vufigWIhw*TJdKZa;^B&fm|#8w0BSApSEr!Qg$)bZYb(qOx!)4f1>Ug zqPC&LtIRiEB#S_?-TR09?H=LYwQ8-pb0ys~+#Pk6uEsyR!$nBnBa^9eucj1Ndw&z(FPIhjx@;5fE^W|NJ zID5C0wAH5jl53=-z1HbVZm@L{Uvir*ndVDACM7d!9lqo~DVbJl_azTX$?RI2E%AFO zXa?2JbKU}ZzGj;ITS;Z)ZW!%18uA}xIMeH7M)1Jg=@>Qe9vP@;w^iqBEJ+X2T+%^z z#WD@L+G@9^GG$@24CD2cTpN~-hQyAS&>(w4ablSvCZm9_7{7#elP@hkv-~WL>TT&& zoP)S06%aGhbqOwLXWQNdX4efjVNd=ay`x>&&f?J}M#Rh=cSD8Dg=ji*R;BKDB9Rz6^40kMR>Pyf$#3s&` z|9WmjCT%~aFbP> zEw?MU$!ozi>Gf1j-6p$CM1Gn-VNp1V^jb_VGm>+Gs;<>SpVI^f$k8co>Z&B4D)=+N zLTH&Nq()h=x?a)Ert+A=sVqD~=pEj&Y;ThE6mXWH~tzy!s7H=GBEepIf7%4j5zjFsQc~i~>MaPm;5`H7#kf`)82pj|m5|Z5Mv^(diSOc>G${_nBAxb~Y z!O}|=D%q-kgZ^eNE_Yu7i}6x3T>_Cj(MPmV&c~cqk&izcR`7|klZ*Q%T%Yu-k?knlFGx!C0I{UkjWrGD zb>pY=y7@C-l?e-8cUbVcL+^E?EAhIKxedq)lel7B@0)y2&f*#tF3EqXTg^q<2FjtA zhAB?3;Y6Sj^7!i2poyJ1pwM3hCV34An{E|k5sS*Q_=Ux59GpmRygHyz6K`VDyjq>rxIAAn@cGSqAn>6?-PbD z;NXCqF9CgI`4WJV8xj98n1zXAFbf`Fsy&iT4_w3N9+Py7qT$s*7MbgInHQ#lXUfZm z^z~d5_6T#qyK`7goVRwd)}a%c)$IAHe=4m9ISs{r!PKw_q!XBY=MX1Txv7x*RLQQt z;RH?c-!LtXsH;YGoOxjcW+2xv*vA1-;6fs!g-rMRJcd0#xG!2~4@e$+k=3)HpgZwd zY250pexIdc#cg%w2`@BqONlpG_G>)p^buG7pQRaBEF}Q>Y9h{V5~BN^ZxX_c+x2i9 z%IS__zh=H!EA(50?6f2iL?(3E9H+yu&b2e4g^U9)n+XvZ@do~-3ei*mT;KiRD%s<% zFeo}<#ts4I`AHHn;h7H=BpzmVjt%;?zVI>ws@Sth&gb8_8vgzWCI(L3L?Vr(_ef`O z`hh{bxKkj4IXM90%Y{~MBS!*Y<6DsGiC?P=@>o2y~-Elj}nw6q8*FP?k7>wn+X99r_ThPNMRvb zHloQYF&4?ALC{t(c8-;;m(U$y&ax<75tIqqs`WNGA0oRjV`fj1w^3}0S7EeoCo!>59;v-`!>ObU*aT?cLPS_T3dGqF^OX=6bcX+JnjuU z6hj*~cj7>dR;3cvQ%=gC609c20=v0kw+b{wewBV8Pm|z}9PpOyexM>2M#JM&h~*$O zZMAIoDO3-km>9En|?!{;HSNP3zLWb3_dN-z*bJ3iy4&^@w)~Dg^VK})|!S#B!? zcVH(GU)S0hzPWaQdFRRsbm-bGU0v&N?Y6ym)@T3Y9rX^BNIM-nZ@iq1mlg(vcj*Vc z;d9q~W?Jh(Yizfm&h8XgXxngWujxJg>o0z>9jZaWYqxM9Ku3M-lR(Vp8==};0oSEB z-}RZ3-cgIU_TF;Ky$^&Z6WaFw1fDt$9|a!T)8C8i#^gA#8ad7`5WP^?2z0t^S(uex zp+2nJc}w#O3V(=Q)W!JWG21wr?YMy&=mgjfAv^TR8?!5+HIb#n-vcE8Kx_-U5cWW| zKK(Sqk~%kKLHCUGPP6iRyccD3JCYsm#GL2cxbl{>Av)P+{K+bUMZj(qerlm>ZkHEG zLA#n;ZO#2lauZr}$5`(N!gCS?2Iw3C0MZcDxikPB8jBFkc%*cpEI=#t z1_wb&CH_G-v~F%wxkt?@R#YeW_M|Ac|2dBxymE)=s-SqIivjmrKQ;NsAUv5E1?Ha> zst34sr&qnc-hg{b(x0&0-h7}+EQA76XGyLDNS52f5*Y3&az;JqMVn3O5fZPs%>vGY z54I#%1R+EJ+~@GRQ_~mNeMs8@&jTb1(mU9xC{YGZ-n*$)7|r9*n~Uc0(?#?AnMd=L zLNw14LRxP#+Pb5o=t_=`;%K}ET~BgMzh#dn(ST-mpvq50R~_uM#Xu4NT&-=zhv!*LNU`-Yz5ROG6Y?3h$Tq+zxI_UjKr~2_7CEKguzi6xZaL-!=~qWd?Y$v-NLuf=aAkWLE7t)&a!G~9 zH7HE|S8R{i%3hw7mjGA;N`uf-9g-3Oow!%@{8dmh`s*(Q661)f9+olk8TV7XaxEF*qUBjwBZgWW-v<5|7#f*$R}uL>L$Xw$ahZbq0i8nHzQF zf*4-HL=}^s$&5ocAj9u7_&aBbS|tlc-EAj2#~;cZ?UhkyvpQ|XKCO9&K%qP4Q8r1> z7y}RLJi-gaqZjDpoWjZgQaLU-2rVwd?9{WUtX;avu?(R8Jz>&N6iU>GXKtI8%+lZ=|9ygzib2(A@E#fui(D2X8+DN52Q5$nxzZdq%p!34Es-^_aQnH~S&r%Dz z56IHc-F5@ZZIUBeBi<%mz=`-JG*Au-dq@D-PIqk(A3M8?bf}qRpk#iXdImT+Q!Uig zEyw5=EF@~08!b$_S#T^pLRGUmwjg7wgBw5j37)55! z)SdU7t>_8MroR@pEQ#JzB6{tuMDM#nz6b?Yb2B7x5YYE<8e}idVKGAJdWBg(m~A-* zspV-sa%Ej$I3RJ|J9Ndyq#fl+T5>&v~c`y4FgdoPp%86o&y<(%azoW|DMFti0 zGnqePV60i-oS3)xxJ^SdI)pd9QoHf&gyoS#TV>(^Hb6^7Wa|+pB&j*(XU~x?`1W)K zF;5}@;SlcgZaO{JA^JNo#My^jTz>G;>NjHzXu^bms26jfWI&`=Ue=vA)F*G z;s?R9q?E{V7b((}+hOegbSbsG-&7|~jIvxwx}Ov2cE$h%GImMA$d)Y(XGik#3x>CY zg;ul}fSk|abD~*J7obudcGvM?zeF21!n_iijR0`I5~ILFfi_Gplc^#+kmATY3FFQM z?UXs1@nUzpDC@{2ckVJncgdZq#GNpf8wgl(S#N|OxhK-{jy!Gto5D(Kt|=*Zg5j$; zBRI<9Ws1%rO z#r}p~VIr1pV(&KqYyL_@i%?ogw~NdQFRWofjAd;q8HDl$ZUr;ke;{UDEl9;ScbKPR zI=^-|VM#!vs+kA=GhR=ctH5GT zUR(=3eSs!$6iHNVA`7XIo94?mp$|>+PbH)74XYqQ^?VIz9M=atC~ux~o9G%~TFe9< z7#6aRuJmdVD0C<(HvIKOO^(caDz1=g`kghId`G9>moCs74#CSnOT;AsOCS|L!96

PzZ{!$M&Q^Jd-^|D+0EgL##cgi2w_~O^u}%6@+8wrhDad`poI+s_ zz(zNqEACKr7buc%bJLEO*d)rSh2mzx?~scX2{BloNntr}Ca=@mI4muF{x(u`FIv@h zfxIp2p%R#nS1Bk7i>>hjnHlQ-+hz7%e8@piU9|HUL+m_=2N4&Cii@hbgYH}}8@x(S zij)0tD@w_iw2lz`1?n<%CzYpm9jZ2R@rbSvp$DriLr7v{2x3?`nG%G)gEFDh$Ru>x zZOP||2{$Bbjvzh)oj|wUgV~KK*?2--5vnk!1o0-{9!rt(Z`hBj$C43$Pbs1Ux}3^0 z$$t@rMF7f7h4C3aQE&pKbHe?<{!ZxJ7p2Y$d%_-cn_uc#!I3^Guy=~W9o2Oq>=S|@ z^1xrG+$LJ#2$h|~8na$>|B(5(MVO68>!@+$t`gLu?g`^yrdsf;mPNil-%2^g^)hhq7us8+mEwta$ruQf)^ zAy0`nT(lu>|8=?E_oQC680^}E3EU2fEH6asUK!*^oOUnPFvCNwiy**2MkEbLTq!sJ z>*pflwxzFu6Q)ifmRyi$$}kvg7S(M9mc<>I(%J?MN0wwZ@JYLJr8U{4 zyf< z#nQTz(s-OBfg$nr4T0W_dyEKchBHb2agGs{6!qn0FirAYA(>ZD5j+LPKEX(j2~)W+ zUS8;1^-eLfI|*Pa>r}|>$)0O$xnvL6`3jVy%J*e9&>y(&Pha7#X_mRo^4-<2r_#d! z2!t%vu;&VxkJDQN!yc~DLL0#lxRcF>g_GUXQuc=b`e`#=b}9JPP05A*0Vg13YM%B= zrFQbG>(UpADoG&AYQ4L#I{g95Y1RB{mr%(BIZsug6-fH;UaB|}l2|$emq%oL z;b#DY!0?n_YNy}0Ecx?_p9Y;zIT4G$P`2s-%f{u3pB5Cs|FtJ!bp;fB^pvng#V#9n zIrfrby9T-`4x@aH|A~UxCL@Z4@|*TUHQ3#>!+P00j>8-bE4t0B&R#5CD<5+&9*ji~ z|JfJ1Ua$V=NfVZ=;6HnV^YjE*%)?_SSW{Yk+~vP_Y0+2?8n!S62=xFT_Jhdv`#-v)IHA zYq&G+qfCHGPxaYv0Cf0o0Pxql1Vg0Jwc@itKZ|RL<>fuXL$yPW0j$Xuf(F2GXBz+) z9nN`_;Fga*(oGhq^S->ew=q zUrCm13ySx7zLKb0vy6W{R@)P0|JAt=uFwXC& z8=Q%++l?cldbbI!JT^Yi;!6r5D?05k zmqU~p+u@((tM&Up;6LOpQt}B#ExlK$9KV!j0%YR|nC{2@DdFz(w-nnDPQ4FBLLF1^N7w zl2`U9rq5ec7hFO?81az)fecGbFUF8yHOaz5!vbE-Sc$90WT<;t53{C)1)JTHoXHgP zY`+qyKmt57A@tR)Q6x=rzey-F&NU+9`;i;`H6r#d z0`ZwB7Rs5)YDfbRDv%J4^iQ-&8Bd=8Dc>dr888sK3>>LAv|*sbV@5oJzZ$YAjmZY} zpgs3lJJ3*Sd6J6(zUD^>1(KW=R4Wz4+!Sx6{sZZeJNJcSZDmH++adx9^RlOTye6wT zM&AXul4D4pMS{2auQ{2}PiR#Yutb3Yn}O!wUS@n?F&yhsnE<$)+IQ; zj#~`2>|f^#9Kq~ee%2P2?!Sm9AaIC7iX&Hs5?8U7S9H~aE$C*!2DWuY?YN2F^_Q37 zx(*$qZ8gs60HQ+|tp*(Uc>rSH3VyU5zj~Q5I4?GOJOQn@9cPo+wKmv$?vnIcsz_UI zALgy~nhJnCXxNRl@|hr3PE=eA`dlSn&3g!@%=<`a^?q`+&=T=X4eqM`R?Cbds@L#6 zNQ@&jlAT&?OTZ73V8S0&Vf-A2>8vE_AE?XWivC`#ScOU(&N@vH{@r_ZdZ(7dg-?Iu ze(sB{IT*hOkXQ8dy7-9NmlUO+W?k{dmaI-cCJ%@{>EGj@W$ABOBCrR-kXS2J-5|Na zlxZXYY~vPi2cR-Mu{8;_{u3wnI8X~vE4T`h566H#X|wy-3jiB25<#C=}j^O zlCrFF;N;NjCUUX}Fa)36>d`X;Idkoo${I{tdLxI?6H{T2xFhC5v81>`~Arwm>%64 z{E6!Lk`*`!vWa_qo(h+ zw}UpGGW`VMN#rWXdoE&qdrv~Di)jCXj&?#7yeOFkB&Y8$Ro7)SK+TX4WCM`^2vJS2 zaubnE(Bs!jJ*vYng&w(^2qTnRZ>E?fZ?sEM*#`s{?KZe`PZHb}&JVb1O>r4c5#Ub8 zYcD2(I_-gL6nskx?Dv0J#iOcC@y|?>eeIumnzgx zDqs-fTwfaJjbx?Ur6{%C=8m2HH4Ya$_Ekc)a}L{z9VNZ&yTlpfLJzb9U;2T>oF>{0 z5~(MF!INoX=~Lzxsffij^!|_x*rkw<*E^9DU;v>jpv3?j69ZW8E%lQAnmo_hJSCmG zPe=1Rx8)-3MudKX4?X`%dYR*C_G?N6hR=xvHb-vLJ23pp=~cvq+@W`&ocfvJR+v(g z-Yp`9V>DEO_^ffczOD3DZsy)uX|I6N{wMH~8R_L^2oO80Ze!#!o zP##*pJIKQ94tpc_lkzTlaj0}^0pC;N?D&He! zmY`3(Q}FC0Fc|cRNwx8VY)nnr3FicV+7~&04Z6pfl-*qyp`YQmWv2L+mHpz6%qsnY zfbvNWnzU#nLn2Srs?mF;(bV8~z{|1Uut)Cb!@*xn0AY@$4W{Ygr(v;3$Y&tH;OvH_ zQEF*n5xLk+*>a0rZd52+kV5!EO{4AMsFD&lB~sWILn$IIk9PJWIOh`izC+@AJvKxoFQ=D0FAUQYBf7{vMS6)ww8CMg0hJz+pB5(Y zYnJv$0W$*b_NGS|CMC|;$WNT1#ht1{z`%L z9%&qG#rJ_u>)rScoehLkc_9Ot%e<97LY#^83y*aNoCd=HdR9>5pB8lBhhlwMGeWI& zDNyNW3sN;fWfka2Dq9iUo}TZU7>shylT(MCi#W z+Q5}XGUPM`?zIC)#g?O5mXJ~9x84Kj`KaZnQc*_LUj0qmEx6sWnX-Xg17Q&}MHTl}>jkIa_uy88uuNuYek5{jyz9xGr7+6iz;00WMq@3Pnq8BR+paFknAm#~x27Tf zle9Q;t6$qVlpn#48wGZb`Fwx5|P4x21g_jdR_Z3#lp?j4J(cCO_TZ4tY>Fo^+0Nu>~(Y5tSr0gE~4sBcd zE2+1I|JwBRgB;gpV6c;;F-mP{c{vLoXl3Mr9?bRro(3E3I;xa&D^hnReZ zv(z(0PMk9l2y?0J5ZJl4)m?>v65V1~4w!PfT9 zOnb*a`t+Hl*EEqR)eOwnIG`gO7eN+x#sy4DVtXGku7mS#K`W?SX-^TO=6@`2h{ zr*=u&^Mz}6xiqth(rla5UXZU<%lG--&yD)zS|ret13VPCz6`hg5!?xJJdv>J*xMXodB8_gOuVXBEe%?Ek~Y+I*4BZRluyyI(|8vE~rN!Hq_+ooQ= zQ|zS?g!DPS-FmMbq`P5|^o#PEIYVn=n^lE^k^WR_+I|kt{RTUkuqXX0YapEol*>sD zI1r5=@4ynJ{dfm-p8I7#-r+zmFLt8~S$|LVpFqki43M&9b$TDcke|4o=PD8g4_*~+ zgyS}ju$Bi#Bi+cw&3K<>pWT3&+oW_qK;b(_+*`oYtg@Vm4-g19pgnf6{Pwhl@$arw zD#ee`a?@T(Wwe4~cXWnN6h9;SBS=4-L$kX=<2S>y9(+vMm%!Cy-MYt($N+r>F0Ry` z+#=^T_Jma(d!I7#geSc-5~~#H7VenCnk}#O)EhWX#5Y*+zu~vz?l=L;b@g!p%6%sl z<8ZUzQq;lX99E+7W2rl;bHvg<3U+s!lQ9DJiO5JxieH4n8m8NWfE`#C`0U5BzjvvU}2ly>FVa@oY*9bFR-%Y@2xB@aMEh6NN-?YH0gk+rKx@U&cY$^MuTLz zE6{x}s)4#m9I$YwpcLGGw?bXmNkv-Y#i!g0F9cS5h1iI|4J}TYp}Lp2|ICl1H$nq# z@H_1?{etV-;ErFcMDiHU*(~FU+DcCi>%07*2FBpmzv&%S+>I1?)?+v*lJ5W&M|WDq z4F!E7fw_&OTD+G0jZK@jSXvcJ;Vf|rrTrrj($IR%*7wG7k?MGA$3d3%oA zSiZXgWJH1lo?ya-_vJ5PSv`fjlY~hA`@JGxQ~46s#e(|AFJa{r%um>s@8UA8++n}~ zwfAF3O6Hc9r`(Wvb zO1Tol^$=8IykkOK{|I_azih_>|5-as!R;pHq0Id21G$gyHZ&#oh53h(#*?%3`-IXV zMYdB+g~h;sRRtK^7=+E>%;u2^$U7;U)$QSuEZhJr=&>wY)XhzwRdP1YMjkXB;*KHO z-wbs^mc%DkEQ9l^K}DAR*nl!Pjzo^3IrdiShaqv6a)W+*3~%(Bavd(x&tzlwXp@%6 zIebcj`i5SVu|$xw%oQF`~R|Xqyo0fVFgL_0Pm-iU(gl)3IWQfim>-LN}`(Pa!S9ZEUL0~T+pL}uMUjg za+us(Fak}w#aqJ6WbZtmnTqMD?pFB>!F5^=n_S%qHT&0{SgY9Q$o`E19aId!=VdE$ zn@zs&O6N&~PuEqnrOby3hix2>QU^VLfuPFdb2uz#tO=$dJ9pGjeup|x`y8jsHHam~ z@yJW+z7a9?AhoC=FYroa{_RZVV-vg;&wWl1<9aR~Yq;*$jBCRhhjW94!hbkT(F;ZD z>`eL?r@2)|_3pnP^ov2zKJFA_7si&!4j0I7BIpj0+tC%F3ch8ozu*bjU!*hlzcs_SAC5F`w)FQ?`A~s}o2-lv%O0m{r9xAKHoWH;j!`0$?c zS3SyX=rbEr!6R5gB#qmir^E<)uQ#2eY70id0X1cW^!uCwH%)^jn)u*@g{%0QP@4Nm zB~2zQt*|1boD!_O8cwO7e`6-6KG_xO&%=qY?KJZ1yC;Z+fH{di8YR88=#2}p91~Vm zKnW5Zq?tPiu!GI|g6PDH(*Mh#Jvjkre?!#$p#u|A-bCHMAvm9!2+ogABrUn+r`arv z{A5bq9s=*4SWe`W3E{)d2v0jWH2{YE$ig|JX?auR(7$u}2MZ#g`?WZTk#$YZqSJ-U zoL~F%6}zGQvX`WCyH@7@LW$#z#PO-L#b`KgC1(zkt0#9Lal1AhPZz^eP_y4#h%^+V zVu1%|ol6I;Cd@=Ot#g?@Svmk=(^dad_k6kWYKdD{0+K|#C4SB0nDiWy zIOK}d>7*Q7rtHJgYFSgO{T%JBGDoXCJP^kexG(LH=J&TwmwnbV+^0ow0aL>=+$Boa~h!GAX43ie1R*Y<{Qpp^l& zq_ydrmb*&ClF9B}^>k0S^5eQZF`rwLg|wCj)XiOG;PXat{Aa1aWMOuVJa<@Wv!AM? zg~`2L3n6;)hJ|1IE29IX`-F7mWYK>~4;c87!tK%(`FgH5<95BruKVaQCAVm}Qv>3% zJ9%ijM5^S!xjMeb>?#4LqFFM`{5{3z@(4{Uy<%|FQ^*WStHqq`XQC2t#|)_~6w;L6 zM=c*|(@!>h^|Ogc7TVr`stYLmx~zxe`XT8y1k_(Gl~fdNIhJL^M+Bf&C-5_NI2Z^G zi2U7Km{VtThBMB17A`2|Q(V3Ucl^tH`8eAx9Csn_fSNerK@X%pSuOwVHpDaXi8x$n z#Zb8_x5xW{m_ZI)z-9;b@reQfi@Eemf+=e0a|Bb7&58;k{%Uzn;F2?*?3<>k70{{V zPwtZqD;AnP+TGULkUm06xqhYnwoRXtP>{bgblwE8Oj26IXbH#1#|r0ham0 zu}m|&>5nO?NP^?zjtzcpp1{f;76|;;o&G$L>ygXjq%$G>U=^E_Mj8+25b+%-qw_*m zLfv4Eg2cDanD6|u0xB9y>7=)Ia$*4%hVlxqM(|Te%E(bFYBA63e%U_pDsQkRdA_8O z&>c_0RRB#{Z@BO~eSlMtX;Se*jHjAqwwF)YIbA$u6qajnUTS%n?#x-v)9vFGt%=2l7=U zXKKBp>KVGhWXav3$v-#Sq0$~FkX%eT{3G{a9d-Jwk^L{Oz1z|9xa?7xt{Y*}5=ejF zSocM#+ivSVOQy1GP)${TB~{yO)gMb$-67jl^+GviTCr7sBvo}YLKCphORur5DxVW@ zdkRhoCdk3L$-c8VR2|<(qjE2<9ovOtXbFBb8%xJ>Cx%~JwHtl(^qcI6C~|BQxR=Oq zkp=e|&dm87O?BOJ%nMYIdVi~9jnl$>`LQWu3FGG6WM$`PMR{5x;r#8pI7cV@NSFEx z|5fpEs*nQcxlNl+M`VZ7bhpDP3%VgC6q*_u$PI;Z$t>ii&@tAm z6Lve{J=&-sH;=oZjoDh~4a>d!4VP#811gS|rFq)B`NqZWsX_^Ra9OXEb&TVW;<=8? zb@dxm4~4~#OHD)sVmj=}o-J}JsPG(#KV9xmwdnSA(H4T%1}a2R5aQ!-&2(^;<+8`E z-4)VBzGkSD)H@)RQs`1^_116!$kxrM9NMIBp)&iZ$Y)rZNI3}|Yw!YiWvI~=Lr%`` z6PSpeZ))Y?J0Z|O*kqR%1*nb*IkO5aCwfA6%pcogCv*0Ibz@v#LH4;)Nszs_)H_VO z>z(V?*3-fUvBE%wO_@GTkUcG~dSD}-t4p1_%MVRjlKw1@_Pn|;?>KdO-0$NOZ4MBv z9v^@IT(`+eIvs7)ba)B!plkX)E35T$sV|b2{mZrj^j}i$=wEOC-YcJ!AECVe3r$NJ zyasjnJkgu%+SJzW>oD0j1fkYE)Uek~uNDV@CpSij_6#__;8<>B2WicEDKXzTc0l)fj_Z4-Fzt(GC7dqp9npCRd} zh$E_LtbFpW3JA0=MrI|A$vr7yPc7t{6Z8jc$ZtpqB&(aY8x+KWYma(&DMs|ZARP2@ zccg)lC5MCh*i_^YAe)NHN|F((`%mUDBQmP~m@OAJ;iWfcbB^%n++GOG4-u%{M-w>k zc;SqziLdZ+X6!SsNwl2UwrY62y_24=OnGTlW<2FuF2&h3c+yEq}3 zP~vVAA?;A_$V=R~TI%ys-8rfgXesf30q>pCV21Bswcv_Y5?ykkyG?pAT_J#UAGbc* zEopC+W!ENU(9ksODL2OLE9Ni+T+d66a;6LUKm>Jh|L0T|b0M6vM^})JC2a*SNI$DP zGs)K?=4UIP=Zpy@G_I2LaO$@b+!fl+h5GNU?DII270WER!Y2^2Epp%d!hEVgAZB&>Hwpd+o-Dmhh1oy3NTcK_$e_)JYfPGb3KJA{7FNU|-R} zj6jaoPWdh)^@eCkB)X#X9?pdR7FQLcvbe2NC)?!{C))a$wzsK^`OMp)x z^tXi+S(Df6?HZBk(_@lYr{S>>RowSj(o@esm-ZVnKq@{pElDI^bt7w};9FgM#K(*5 zhlu1N9NrTRa^toAKut}Y&B6kOy^crry>LY}uZya9*&Z@Oi0?k)8h=r>_lbkEN;e*+cX1aC~TS;7dz3*#m>=F zVMrj;@ucGb1{@$HE@VwV>{J)%jiN(EmF{}{@ zb5bRo(Ez!NcapyX>`(vpDV&LZp z9Vo+xJ^W9kfC`ZE!L%r@dv%|5LgCI~x+k<|k0mw)AM~w9`qi>J?+BFr>ge-5N^|aB zx4Ix^<)vIH;0CXyRIoCN%DD_))sb=Q9^O{HT0r%GX923?RsMl8>F7XsXB7K?#~<8P z{C4gB2>ii)T8E+;#?6}%h8{{eK9&kB5EA&ss6jZLW>{`#j=_Ke{cHy6V)lpbSy`^$ zAn%^rxw3puXy8!sB&~RdG?g-V9DBU6Q?CXs@45X6V|8t=R{p<1)@6>Y%Ye+6LDsjA zOV)i!R?w4V1$F;#BkORkHJkK|H>C}78h{O*=7B}GQb}Iwgj}e|p$aUcd`pdd*h&d) z?|2hvX+O-hh2XErJ#jjyQU7qA>8GN9?8-1527DRG&Zsog#h9r5=9^3v|12#@U=25W zc~n3@h3SW^rtzlay@ina6$0tV0avjECJ7>-;h$#xNH+tdaj=n49=PiY7@Rt#>}nP9 z=0M5;(jCSC2@IqkTtGb<*}JWgZETXCR6;C4claf>KRG)Uvl2}wzFd^wWN7TQoJD_- zik>V>zr}u>PZ5W&K3O+)8I25>+PkUCI1`z|a-BjBFR~v*fP;6N)2GSXa=YHNYUb^F zdOqFe!oeZEX&$%|jg7mpEi0wYCH{w20i;_SR_7O4KlTd8@=Uf~n5>*Y^gSq?u(X4J z=S(Gc#gcGc?wjI1);re{(#zp7Q<*L)D`TV|AY@?=JI9^!+*tApNso5AeJy;`nI8o7 z^S>-8hOAA(lurjlIa|@7#I%^Djf6p@V@kr|0|J)l`0M~BCq2V?K1+msUmq^(1`b{y zF4YhPOe4WwW2o|;!UE8EP+6Hlchof&Ig zDD`5fz`b>1_D`HJE{*1I`FMiMZ6G&jRg4$?&la))O(HfCov#!+7fn4F%F$UkoZlQt zPw~1|<>)l4qttf6;9?eC2rW)!c|As4UW>62C6DOI>5TtzuvuU)9G~#Act-)PO{AP? z64%Qld;}$Oh;P;J0p73opB|?{EI~3EKb*6<(Jr0S{9?H~mt?RY>DTSS- zO{mXP#0?A%5oDHY79w7BOy# z;8kjnzBG=-cm)=zFv$28>9WE%Ci$%ivqW-OO?mYOm4Io$F)Q*CECF%d1rczcyvCzQ zID-^4SmM6yNlJ!`aVJ9VtKjugu-8bD7vbK41>_BKDb_s!ol&x)Kb z77z8g1*n^>>qL$c0u#Y(*u$}OK?`UVhovs7)WkmDr6`uqr|ZmqL4$(DlH6(YMzi?I zwPq2?BNe#pj;i8B_#{OmBltf7f{CUO@V zmoTERhv&)dkr>(;}&ffTgL@1RkEZ^>E zEcn|U-~6!{MrTAI|LJsH@&R@Alb$~OApTHh3ZEkY%PK?hW7CmaVR(gn)T~SkV&@pb zdtn-sgVWrESfh!LEZ0m;p{~pmBu$zr*9AzzoU<7k1*p|L0AqR?2N?tFZ+XPJUA&eI&}TQ4I`<9USIPLLNr_4M2$xZ8$Ve3gxF?2`+Pr3bW`{ z5nz(`I=|BcfhGKzUtK1mbV;POB zk@ntohN{|pu-0EHs4LF*wa1OjmPvXYNa2yYiVzXevh1nZMCIOTBSD%46gAw1IW%OD^bazIRbMZ zyzC@kcaWaJ02y)KRCb{))?H}W2yRc*fV{dNZT(b~ReG*7Nzi=U<3nzy`qPDrKzFnY zSMwG$m@JH}pEK6`T~q)w&!+z~AGJyQ=BR599U)>Cl!BeQ*!J!f@YD=Q`J5SX)w1_X z*?X?g{2-%wc+@n7oHXSfYXLmE^03TfM9lu2rx@k~&P699|TrKlYE~PsT|s zCi9eh7BqE}$$l-*uXfMhX_#CSv8_-v6T20GC!SJqKK2Z(25g}2G|K^9 zgohvMd|O;yHrd>M+%UK1IIdxG;9msOfwC|ja70~vc)uYB(BVJq)~SbkhBfjJHW5q> z5u&G_;#};hBV~CRCDro6AvP5IaB6ZQUfnR!mqWyt!IhkRBTM3}xy(t7t-OWX!V<)s zxz<`41t$k``KxtdEaH0uI4@0Vq*-w^@D&8*2OMK-H;1t|^YU}1O%9UX)t$au^>liZ z0Q(66@MSbmSEBj23AR`_j&QR8W?@>leb&ZyN_`6c!@9mg?~P}e-cT5&i0FTs00}^= zLxTVb$Ls>R8NSOYgXBN|itvU#^fhcs&O+QgWD)px1hLB+6e6MzQ8U1<$axTta9W)o zi2Vumi@E%QEnR{43a%>Y7pX(KD>mhfj1afCb`hi$Y$_{FC_`rU+eD>FZY|tPwOyl! z={ML@x4Q^9o(LZ7K;(|W#i+Amwx|{@689_nwB&@Y!0c z=TTY|DMMKlg%(BrCLMI2qlgze!9a4ruPK7o=FVRMKX=pEf-3H`&MDDerkdyZbBajL ziSwPUQ@xmz)ca~g$5vPCXkXUR3hN+Eqvg|64zO@4<)NhPaydSDpMI0QK}GyU%}ege zezlNsZcg%gZp)q$ir7nS#n-W12Lh_k%snA?Xk_p>I%bJH9h8(E9TVFf@#4OEGqkR8 zFeQ8-`jB;ycDOs-J}L_xH(BGS#s}Y0#nKq+(d;ahk5Lk*Za#iRBl)+SJWcAq6~d6J zc2MKzL&OszgI>%keUC+!cRK%V0;e-4z3Vakk|G^WaiQPzgY32G0DLl<_ni~Ez)}}-=>}kdG6ryUxl)AtjPb2%2re%K2l)I0 zrmV&27mEd{zs;3$$MpfXYiFwruhmezY;}68l(dlz%C)fj4TVr8HT-Eq|6va#7l+q~ z1~{1ZR+ARkSiX9R%s*dUK7IlgY9=%1OOkY^UnRXU79OByq4U#+St4$^Qw6+AMGv83 z2A;8AffD-_6%Qt=dVW$R_8cKH33hZ&dd$Z;Bmfi~EaSMj+UfmClpe0-rnG#U@@m&4 zJ2R|F>a|$DKyzPTur9VT#SuJeNQ7i5cin57%uU}l2> z!o32-_*B}89-)aC5%L`opFTrGw9<$V7LsVqo^rnWGl52gl~#x<0#O|hRrU&M`1Ai% zcdapYRaN*t=6#DCNh48A+%xn?P3WYZmZq6Bv1j_gF>R4{s{Sy!IrpADcTVTtb8gRL zrqfJH4QMe6NC5F4n23)b#Gj&x222FhpkOM&L<~QG7){g=q9G#nTWjxq9{1iES{{i~ z?woV>*^jl?Yp=b|-fKg>k?!A?0>+<+^(R7F!B5g@V1q`*^Ul-OUlgoRc=(@_aLp^k zoqY;1wVG>zKK%JcCFPr7RPU0@EDAKW0SYVDUpKCBQW-1*no=U8+M!QvM$6CX zn6WVIpi`mN5EPWJ2olTJQGFfL*YUK(gigVCW>LlH3Z&3?GYUC7CdS2ts9AreDk=xW zxEe~JQN~puFKnrI?L=P6-iAzbotNjf0bm{K)xlBi(jNMJ^Hx+A}BjH#5kX?PC3dwLvh8nt_RT#Ti6 zW=HjSI+ZsAGF0QM9Nb9QCVt14smW3;w}7&bT?#`|K}~NlQ*0pYs>h_W?>s`lx%O|i z3#)o#R2jGf^WP7KU?kBP$Z=-K5V^=&KHJ30cr9CxYUP#+$Gi1^nufE!LNLBZwGZ%r zKA)TB^kFoWiXL}ozI)Wl7(CM4J(_fpZyC{Op^dmrWeCyi0fFBT$eyDw(|ZjQ)@Jb& zSme6|BZ34|VGL~d(j!=`nqQkqJXp1FKdsR;lWc$P70*$cEOVP6iI94c{UL6)Y9n#oXilAH%c+3WJ8fw>5tnA+6=6WSekKzxN+l z)$Kw?zy`litl1X`>Alxa%u=?V+MxblRW=Dmp0WPM9U%3GCTKaQ^-F4Gs>({SazMSo z!V^VfoK!*w-a*w@iJ&YZ#P-7$f0mdW1$|k5C>z!%LU!(BY>(>5CJ01f|A+VfUuWho zz0YXUR6?tndtY;-Xprl*ly?pQkR;thO`$kL2b&S2QPp~y5^0<&ThGv~Dgr0_;4o|7 zFIR@M8PrC$6&MMoTgm<^g`Bls*VX{eJ}BE4^GUf?&x|cg=@o3GpW}%Yb%q^DTTSpr zxJN{V6bwzmI1cP%lfyOi*Kk$JwS7kknaB)%hyL9`hJ2~kSKz#M=A|d*Xpypiz$fz! zsiU@PpQ@0Lv*>}GD^CK`)=SiLgot|^=SPj^W(@`Z<%R*@c!FWRHa=H|WmCyw74)G# zW8}AZWugvziU|F{WjambscKnNZo5WrwIUThS6+u=f0X?Xw0?+#Rq8EM=3DrCi1VxG ze_D&B1hS#Lidd^00MFdX-;77p06T^0rTsVVPa0xZnBlE9RkD=dOA|(UyYdnD1_mZ7 zejuAHSq~`fs6Vl~SE5u^869}-o2oop<7DlT+*=n|uyJddgZv;BY3FlJwX4yogPPSM zLK^UHYIDHqna2CtrJG?H@}8=EhklZqMQ*~I{Yn;7Q^tQDmqxMT%d}c9`!(vAj;p{^ zLZFpaEMQW^Ul}ixH7!V|v0<{sdcXXM@*Q^kVuK~KLz{P)xmym&Y!t6b!he3 z0E4j1u0bWH#sdRduz*r! zv7Aj4Hvy_=)&Z;!1_}l@^mfkzlC-Tyb6(3$8Y`}qg)AFqfc{W{%T~Ruf~gH7|DwSO z*4VM|^ZqLP8ID6ap+k_qo+?oicG|ziYfL}ZJCb7=-o|?mGP`6Tn1VPnn~Z2MnU%5F zF$L37tLp&9R@hVD_)SS~t|ETkcG~_~0q!4VN{rsJamy>d3+wT6!uQB~z%%?S)B$)* zXe|{M1z=uBP+``ULT}u}yzdJnx1YP8)~x8Go~<`Dyvz$cOX@>0edWBVe#_ARk^icYo@qE_tt97u{BlWUFIa=Q`SM0qm!jNj zWVqYo*0a<`1q9`x&;A|9pvYOG&=%_4OcYDK1Xtb4rFYZHrJOIKPd8{B11fxPD%sC6 z+{4y$Y9^*aJfKe>Cx0gZ2j@J^Sw^xfn2V7aRyknbt_%TerM(4rL-RM#;w>HXm-t*j zePY(|0`-Ee0<~cQgCuR{#DoG0_4o9Wg8!B==Ty9eQr6w7OlBAVANNYJ`@mjZ$nCG` zW4w%3JZJ8s74Vp1br7L|Rw9%6XUGNToFdoMaGqcw#;w;>Df;g)6hFQgilJpKP|q_l z%1RIsdq`93*G#$!#mLk%We^e76pK|&vBKV}R3_H48Au-j^aue?WCWN%lA2jr_80+q zX{34K0BIcRm&W&{<(!0GdCNZ?&DOQNTuQA@B9!$oA$1B-fcaXneoj}oHL$CmlpKyN zQl5R1Pk8o^Vo1S89$Gkn3Hc%Q1hqt-{r$M?KW+V;1`*l^OAr6xhvKZiLL)tnd zh#~mY?fAg0#&msb_5tJhFIUjWj?z(bXLDnhUZsHx|7J@s(k*@7RZb>&W~^^hFNjtG z1-PYr;ulk=8Py6fY+mqU-(WqWd(Vk#EzBo$Vp-+nRU`IwI+3ulowkTqtuGlvX8Yl5 z_$%#B)@l3si`h-UV+s?RC+wgceJAfJ2WSZHo9NHZ?B#xxu^(su!(QW%8x&J_o2rtE?at;5J%T{>5G-eWJ1p;7(kPuBNrtO=Q34U`v z843-92XgJ(e_|V8#7a*z&B1YH0DwFdrXN7iYcPNn4SOCas4fi@_usrC6u!VOpff&j zXaQ^4{t7!y%~=Dy&QC=W$gr3~MZWZxOM2pkPf!o@cJGh&eu8?k0=F>VcO*{+_M!j{fqyjF+A(z1}r5g30}F|wls`M??s^4`PAOUs&Zstr8D@Z;0*jYG>* zqxSllsWH9HUO=Iqo*GA;BsZ9VeHx;Q$CwVaIRnIxQ|<}4(I?AjePo$Zhr|S?nA{{E zVYnj7c*YWxp%^Erz$oSHaQ*TL+T@pp3a_c!@nvwb3|O%j5@iPGM&40CM6C+|%0c>} zL&)`7LC3kLW!+PJRzZ%Qo|bB#!Mwoc@rffxR}P>M)Ctyq3o1D^toFR3EP=~akT#-y zOiCeP4!KNLL$&rHARTDJA=;Qbmuh}_>jV~x^o@hW=9tuTZd{XpQnzVUth;k zgRJ!jx%Jwm>oK{%aABBZ=Sg8g0pMjyc_~3_ZV_#3Bv? z(GKv^Uvb)=EATgwu55b^89K4V`>t?hGjOGlVHkuxucSZll`o@Mx+4F!<3*9@x5Tns zi_kb4J14_J)w81X4*z**EevHd7JkwJfFf8Fhff|_5OL`E(V`3^Cu}7h>BohN#r99m zip9il#$MnHH;|F=gIGAtrtHS5g08bh#RwaVa#fgWB1-5u{#ur@8UkCn_S3A=ce{ay zrb8Je?YPkSSEZa1 z1#`>k$Iz5cw_W$P#PMSb61$#CLqOa1zBpwG%;GMyNISc;xsk0Pf_Gg=2>o>`=Bm&_1+% z_oR&NwkiPHC-UH2{ov7CPo6q)xEc5g3+P|F2T>^tl+(NHrgHgmwB|Q8)(q-J07eEY zfC*YMW9w)s2+_BQm|S>!_b3ML&dluT&fL7WyL;cB?#(m179Fn*Rz;R};PADv2EKQ~ z&{;d?_)bf@pOa1D`K~-$Xo$r@@fqgb;Y16cr{A%7}}w7?(RHMO{_Xu4x*QIS&4}GLuo-&KCkshOsWU(%(E6O;1}t~ z5)gxVK?EUzq>`zsMK6qE7c2|WsdJWkvr&BZd@rs?Nw?cx!@E=dvLCGax(3g+mFi+} zNz%HZT=9Y=YOB5zWu}`!LKI;Pf@VO5-;@Vcq6k)GxY!O>r^UjO7l}^bCT&S_E0zMt zCTep*2q_i;ddE5Ib&`%q_RY>7v%er*ZxPyu^p5F&l!qrr1TcYco!IFiH6=sUdd;46 z!vc8~EVhGHX2sc#(^-LVc2?Y^(^;cRArLdrjCIr&H5}0)bjvdd7_bpF>s`l3{TL78 z$ZPqehnJjasUEL&B?{vI-F|@nw%ZM-xt!_CRWDu=@sb0j>vS**oP~Tx;A3H=conLL zG^)P?8a-GFR_mlOm4+tfWQGjlB^j=I5#+NOO47BOxPYUcrZL+&iY@6fLw|O4R%SXY zy9yH60MXpY3%YTgvfP$PaH{Nd5BLcJSA9*quuSSRtvI0Es9f((3@ITLI^^+G{YA2n6#fZdm?b?%@RN7?4i zad(dgLg$ohTLfUHf4aUXffDkv&cV8&@P;NbBU zkgo2iOG90t-Ri`C*%Pxe>C-xK55ZVRrYwOmMoEMpsWPQ$&~M-%GEoR?>JE%<(v-SX zWP&A8pGkF z@~Io(SAu0+^)%4>4Go?6WO#+rt!EfnU4m>;1IM^FuzIh7$NY9u#)pkzLb@#Qp%B5wN|TGm znt&3O;XCAktpI*$vi^(+@OhX1wlTQQyG9f14i)5-hva!E0k*hOm<@76x$1;24V`3H zq;|~@Cn}{6W7IFaAQJ^?&-A>5>7lG0ynChpot@c6$WW`4K9Z|f+f*fi^lOXhD0k|_ zJks{zT^<&cIn@QX%gnwmEg;Vgy8MDRMX5t)k!~SfY2Kg-RRkiIN`9qMI)h(~-zt7p U Date: Thu, 12 Sep 2024 15:46:33 +0300 Subject: [PATCH 09/17] Add one more test --- src/smartcontracts/codec/managedDecimal.ts | 8 ++- .../interaction.local.net.spec.ts | 66 ++++++++++++------- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/smartcontracts/codec/managedDecimal.ts b/src/smartcontracts/codec/managedDecimal.ts index 0718dfa7..b5a494b0 100644 --- a/src/smartcontracts/codec/managedDecimal.ts +++ b/src/smartcontracts/codec/managedDecimal.ts @@ -29,7 +29,7 @@ export class ManagedDecimalCodec { const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigUIntType()); const scale = buffer.readUInt32BE(bigUintSize); - return new ManagedDecimalValue(value.valueOf(), scale); + return new ManagedDecimalValue(value.valueOf().shiftedBy(-scale), scale); } const value = bufferToBigInt(buffer); @@ -41,7 +41,11 @@ export class ManagedDecimalCodec { encodeNested(value: ManagedDecimalValue): Buffer { let buffers: Buffer[] = []; if (value.isVariable()) { - buffers.push(Buffer.from(this.binaryCodec.encodeNested(new BigUIntValue(value.valueOf())))); + buffers.push( + Buffer.from( + this.binaryCodec.encodeNested(new BigUIntValue(value.valueOf().shiftedBy(value.getScale()))), + ), + ); buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale())))); } else { buffers.push( diff --git a/src/smartcontracts/interaction.local.net.spec.ts b/src/smartcontracts/interaction.local.net.spec.ts index 13e0780e..b3d5e589 100644 --- a/src/smartcontracts/interaction.local.net.spec.ts +++ b/src/smartcontracts/interaction.local.net.spec.ts @@ -185,7 +185,7 @@ describe("test smart contract interactor", function () { assert.isTrue(typedBundle.returnCode.equals(ReturnCode.Ok)); }); - it("should interact with 'basic-features' (local testnet)", async function () { + it.only("should interact with 'basic-features' (local testnet)", async function () { this.timeout(140000); let abiRegistry = await loadAbiRegistry("src/testdata/basic-features.abi.json"); @@ -210,23 +210,23 @@ describe("test smart contract interactor", function () { } = await controller.deploy(deployTransaction); assert.isTrue(returnCode.isSuccess()); - let returnEgldInteraction = ( - contract.methods - .returns_egld_decimal([]) - .withGasLimit(10000000) - .withChainID(network.ChainID) - .withSender(alice.address) - .withValue(1) - ); - - // returnEgld() - let returnEgldTransaction = returnEgldInteraction - .withSender(alice.address) - .useThenIncrementNonceOf(alice.account) - .buildTransaction(); + // let returnEgldInteraction = ( + // contract.methods + // .returns_egld_decimal([]) + // .withGasLimit(10000000) + // .withChainID(network.ChainID) + // .withSender(alice.address) + // .withValue(1) + // ); + + // // returnEgld() + // let returnEgldTransaction = returnEgldInteraction + // .withSender(alice.address) + // .useThenIncrementNonceOf(alice.account) + // .buildTransaction(); let additionInteraction = contract.methods - .managed_decimal_addition([new ManagedDecimalValue("2.5", 2), new ManagedDecimalValue("2.5", 2)]) + .managed_decimal_addition([new ManagedDecimalValue(2.5, 2), new ManagedDecimalValue(2.7, 2)]) .withGasLimit(10000000) .withChainID(network.ChainID) .withSender(alice.address) @@ -265,19 +265,32 @@ describe("test smart contract interactor", function () { .useThenIncrementNonceOf(alice.account) .buildTransaction(); - // returnEgld() - await signTransaction({ transaction: returnEgldTransaction, wallet: alice }); - let { bundle: bundleEgld } = await controller.execute(returnEgldInteraction, returnEgldTransaction); - assert.isTrue(bundleEgld.returnCode.equals(ReturnCode.Ok)); - assert.lengthOf(bundleEgld.values, 1); - assert.deepEqual(bundleEgld.values[0], new ManagedDecimalValue(1, 18)); + let lnVarInteraction = contract.methods + .managed_decimal_ln_var([new ManagedDecimalValue(23, 9, true)]) + .withGasLimit(50000000) + .withChainID(network.ChainID) + .withSender(alice.address) + .withValue(0); + + // managed_decimal_ln_var() + let lnVarTransaction = lnVarInteraction + .withSender(alice.address) + .useThenIncrementNonceOf(alice.account) + .buildTransaction(); + + // // returnEgld() + // await signTransaction({ transaction: returnEgldTransaction, wallet: alice }); + // let { bundle: bundleEgld } = await controller.execute(returnEgldInteraction, returnEgldTransaction); + // assert.isTrue(bundleEgld.returnCode.equals(ReturnCode.Ok)); + // assert.lengthOf(bundleEgld.values, 1); + // assert.deepEqual(bundleEgld.values[0], new ManagedDecimalValue(1, 18)); // addition with const decimals() await signTransaction({ transaction: additionTransaction, wallet: alice }); let { bundle: bundleAdditionConst } = await controller.execute(additionInteraction, additionTransaction); assert.isTrue(bundleAdditionConst.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleAdditionConst.values, 1); - assert.deepEqual(bundleAdditionConst.values[0], new ManagedDecimalValue(5, 2)); + assert.deepEqual(bundleAdditionConst.values[0], new ManagedDecimalValue(5.2, 2)); // log await signTransaction({ transaction: mdLnTransaction, wallet: alice }); @@ -292,6 +305,13 @@ describe("test smart contract interactor", function () { assert.isTrue(bundleAddition.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleAddition.values, 1); assert.deepEqual(bundleAddition.values[0], new ManagedDecimalValue(9, 2)); + + // log + await signTransaction({ transaction: lnVarTransaction, wallet: alice }); + let { bundle: bundleLnVar } = await controller.execute(lnVarInteraction, lnVarTransaction); + assert.isTrue(bundleLnVar.returnCode.equals(ReturnCode.Ok)); + assert.lengthOf(bundleLnVar.values, 1); + assert.deepEqual(bundleLnVar.values[0], new ManagedDecimalSignedValue(3.135553845, 9)); }); it("should interact with 'counter' (local testnet)", async function () { From f235986e5f28daca8dc6da281e25437cc153f86a Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 12 Sep 2024 15:47:53 +0300 Subject: [PATCH 10/17] Update workflow --- .github/workflows/test-localnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index 2f180234..8d20a4df 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -62,4 +62,4 @@ jobs: - name: Stop MultiversX local localnet if: success() || failure() run: | - mxpy localnet stop + mxpy localnet clean From ca53fb3fadf7ab7c17423b001ae19231ee1b8bd1 Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 12 Sep 2024 16:01:01 +0300 Subject: [PATCH 11/17] Fix test --- .../interaction.local.net.spec.ts | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/smartcontracts/interaction.local.net.spec.ts b/src/smartcontracts/interaction.local.net.spec.ts index b3d5e589..cc810fd6 100644 --- a/src/smartcontracts/interaction.local.net.spec.ts +++ b/src/smartcontracts/interaction.local.net.spec.ts @@ -210,23 +210,23 @@ describe("test smart contract interactor", function () { } = await controller.deploy(deployTransaction); assert.isTrue(returnCode.isSuccess()); - // let returnEgldInteraction = ( - // contract.methods - // .returns_egld_decimal([]) - // .withGasLimit(10000000) - // .withChainID(network.ChainID) - // .withSender(alice.address) - // .withValue(1) - // ); - - // // returnEgld() - // let returnEgldTransaction = returnEgldInteraction - // .withSender(alice.address) - // .useThenIncrementNonceOf(alice.account) - // .buildTransaction(); + let returnEgldInteraction = ( + contract.methods + .returns_egld_decimal([]) + .withGasLimit(10000000) + .withChainID(network.ChainID) + .withSender(alice.address) + .withValue(1) + ); + + // returnEgld() + let returnEgldTransaction = returnEgldInteraction + .withSender(alice.address) + .useThenIncrementNonceOf(alice.account) + .buildTransaction(); let additionInteraction = contract.methods - .managed_decimal_addition([new ManagedDecimalValue(2.5, 2), new ManagedDecimalValue(2.7, 2)]) + .managed_decimal_addition([new ManagedDecimalValue("2.5", 2), new ManagedDecimalValue("2.7", 2)]) .withGasLimit(10000000) .withChainID(network.ChainID) .withSender(alice.address) @@ -240,7 +240,7 @@ describe("test smart contract interactor", function () { // log let mdLnInteraction = contract.methods - .managed_decimal_ln([new ManagedDecimalValue(23, 9)]) + .managed_decimal_ln([new ManagedDecimalValue("23", 9)]) .withGasLimit(10000000) .withChainID(network.ChainID) .withSender(alice.address) @@ -253,7 +253,10 @@ describe("test smart contract interactor", function () { .buildTransaction(); let additionVarInteraction = contract.methods - .managed_decimal_addition_var([new ManagedDecimalValue(4, 2, true), new ManagedDecimalValue(5, 2, true)]) + .managed_decimal_addition_var([ + new ManagedDecimalValue("4", 2, true), + new ManagedDecimalValue("5", 2, true), + ]) .withGasLimit(50000000) .withChainID(network.ChainID) .withSender(alice.address) @@ -266,7 +269,7 @@ describe("test smart contract interactor", function () { .buildTransaction(); let lnVarInteraction = contract.methods - .managed_decimal_ln_var([new ManagedDecimalValue(23, 9, true)]) + .managed_decimal_ln_var([new ManagedDecimalValue("23", 9, true)]) .withGasLimit(50000000) .withChainID(network.ChainID) .withSender(alice.address) @@ -278,40 +281,40 @@ describe("test smart contract interactor", function () { .useThenIncrementNonceOf(alice.account) .buildTransaction(); - // // returnEgld() - // await signTransaction({ transaction: returnEgldTransaction, wallet: alice }); - // let { bundle: bundleEgld } = await controller.execute(returnEgldInteraction, returnEgldTransaction); - // assert.isTrue(bundleEgld.returnCode.equals(ReturnCode.Ok)); - // assert.lengthOf(bundleEgld.values, 1); - // assert.deepEqual(bundleEgld.values[0], new ManagedDecimalValue(1, 18)); + // returnEgld() + await signTransaction({ transaction: returnEgldTransaction, wallet: alice }); + let { bundle: bundleEgld } = await controller.execute(returnEgldInteraction, returnEgldTransaction); + assert.isTrue(bundleEgld.returnCode.equals(ReturnCode.Ok)); + assert.lengthOf(bundleEgld.values, 1); + assert.deepEqual(bundleEgld.values[0], new ManagedDecimalValue("0.000000000000000001", 18)); // addition with const decimals() await signTransaction({ transaction: additionTransaction, wallet: alice }); let { bundle: bundleAdditionConst } = await controller.execute(additionInteraction, additionTransaction); assert.isTrue(bundleAdditionConst.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleAdditionConst.values, 1); - assert.deepEqual(bundleAdditionConst.values[0], new ManagedDecimalValue(5.2, 2)); + assert.deepEqual(bundleAdditionConst.values[0], new ManagedDecimalValue("5.2", 2)); // log await signTransaction({ transaction: mdLnTransaction, wallet: alice }); let { bundle: bundleMDLn } = await controller.execute(mdLnInteraction, mdLnTransaction); assert.isTrue(bundleMDLn.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleMDLn.values, 1); - assert.deepEqual(bundleMDLn.values[0], new ManagedDecimalSignedValue(3.135553845, 9)); + assert.deepEqual(bundleMDLn.values[0], new ManagedDecimalSignedValue("3.135553845", 9)); // addition with var decimals await signTransaction({ transaction: additionVarTransaction, wallet: alice }); let { bundle: bundleAddition } = await controller.execute(additionVarInteraction, additionVarTransaction); assert.isTrue(bundleAddition.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleAddition.values, 1); - assert.deepEqual(bundleAddition.values[0], new ManagedDecimalValue(9, 2)); + assert.deepEqual(bundleAddition.values[0], new ManagedDecimalValue("9", 2)); // log await signTransaction({ transaction: lnVarTransaction, wallet: alice }); let { bundle: bundleLnVar } = await controller.execute(lnVarInteraction, lnVarTransaction); assert.isTrue(bundleLnVar.returnCode.equals(ReturnCode.Ok)); assert.lengthOf(bundleLnVar.values, 1); - assert.deepEqual(bundleLnVar.values[0], new ManagedDecimalSignedValue(3.135553845, 9)); + assert.deepEqual(bundleLnVar.values[0], new ManagedDecimalSignedValue("3.135553845", 9)); }); it("should interact with 'counter' (local testnet)", async function () { From d6eb8b2613ff1a61a84f9cd4ac5c71a40154fe52 Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 12 Sep 2024 16:01:37 +0300 Subject: [PATCH 12/17] remove only --- src/smartcontracts/interaction.local.net.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smartcontracts/interaction.local.net.spec.ts b/src/smartcontracts/interaction.local.net.spec.ts index cc810fd6..e1b546da 100644 --- a/src/smartcontracts/interaction.local.net.spec.ts +++ b/src/smartcontracts/interaction.local.net.spec.ts @@ -185,7 +185,7 @@ describe("test smart contract interactor", function () { assert.isTrue(typedBundle.returnCode.equals(ReturnCode.Ok)); }); - it.only("should interact with 'basic-features' (local testnet)", async function () { + it("should interact with 'basic-features' (local testnet)", async function () { this.timeout(140000); let abiRegistry = await loadAbiRegistry("src/testdata/basic-features.abi.json"); From 4f6a84cb0398be0c74693dd686f5418c65764e7b Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 12 Sep 2024 16:04:58 +0300 Subject: [PATCH 13/17] Update signed codec --- src/smartcontracts/codec/managedDecimalSigned.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/smartcontracts/codec/managedDecimalSigned.ts b/src/smartcontracts/codec/managedDecimalSigned.ts index f6e0654d..6e79900d 100644 --- a/src/smartcontracts/codec/managedDecimalSigned.ts +++ b/src/smartcontracts/codec/managedDecimalSigned.ts @@ -30,7 +30,7 @@ export class ManagedDecimalSignedCodec { const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigIntType()); const scale = buffer.readUInt32BE(bigUintSize); - return new ManagedDecimalSignedValue(value.valueOf(), scale); + return new ManagedDecimalSignedValue(value.valueOf().shiftedBy(-scale), scale); } const value = bufferToBigInt(buffer); @@ -42,10 +42,20 @@ export class ManagedDecimalSignedCodec { encodeNested(value: ManagedDecimalSignedValue): Buffer { let buffers: Buffer[] = []; if (value.isVariable()) { - buffers.push(Buffer.from(this.binaryCodec.encodeNested(new BigIntValue(new BigNumber("23.000000000"))))); + buffers.push( + Buffer.from( + this.binaryCodec.encodeNested( + new BigIntValue(new BigNumber(value.valueOf().shiftedBy(value.getScale()))), + ), + ), + ); buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale())))); } else { - buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(new BigIntValue(23.0)))); + buffers.push( + Buffer.from( + this.binaryCodec.encodeTopLevel(new BigIntValue(value.valueOf().shiftedBy(value.getScale()))), + ), + ); } return Buffer.concat(buffers); } From f15a44ba2f5cd83a056a16cf3a7b66644a57587c Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 12 Sep 2024 16:20:12 +0300 Subject: [PATCH 14/17] Update workflow to stop when finish --- .github/workflows/test-localnet.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index 8d20a4df..0bb7c4d7 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -41,7 +41,7 @@ jobs: # Start the local testnet with mxpy mkdir -p ~/localnet && cd ~/localnet mxpy localnet setup - nohup mxpy localnet start & + nohup mxpy localnet start > localnet.log 2>&1 & echo $! > localnet.pid sleep 60 # Allow time for the testnet to fully start # Step 6: Install Node.js and dependencies @@ -58,8 +58,8 @@ jobs: run: | npm run tests-localnet - # Step 8: Stop the testnet - - name: Stop MultiversX local localnet + # Step 8: Stop the testnet using the stored PID + - name: Stop MultiversX local testnet if: success() || failure() run: | - mxpy localnet clean + kill $(cat localnet.pid) || echo "Testnet already stopped" From c113ff5027afd42b48bf87ca520c992e27721571 Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 12 Sep 2024 16:32:32 +0300 Subject: [PATCH 15/17] Fix variable name --- src/smartcontracts/codec/managedDecimalSigned.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/smartcontracts/codec/managedDecimalSigned.ts b/src/smartcontracts/codec/managedDecimalSigned.ts index 6e79900d..0f92fd7e 100644 --- a/src/smartcontracts/codec/managedDecimalSigned.ts +++ b/src/smartcontracts/codec/managedDecimalSigned.ts @@ -25,10 +25,10 @@ export class ManagedDecimalSignedCodec { } if (type.isVariable()) { - const bigUintSize = buffer.length - SizeOfU32; + const bigintSize = buffer.length - SizeOfU32; - const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigIntType()); - const scale = buffer.readUInt32BE(bigUintSize); + const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigintSize), new BigIntType()); + const scale = buffer.readUInt32BE(bigintSize); return new ManagedDecimalSignedValue(value.valueOf().shiftedBy(-scale), scale); } From 490dbecb229acf026298773b5a119ad9f168c198 Mon Sep 17 00:00:00 2001 From: danielailie Date: Mon, 16 Sep 2024 10:39:36 +0300 Subject: [PATCH 16/17] Extract variable --- src/smartcontracts/codec/managedDecimal.ts | 11 +++-------- src/smartcontracts/codec/managedDecimalSigned.ts | 15 +++------------ src/smartcontracts/interaction.local.net.spec.ts | 2 +- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/smartcontracts/codec/managedDecimal.ts b/src/smartcontracts/codec/managedDecimal.ts index b5a494b0..0e2c5e43 100644 --- a/src/smartcontracts/codec/managedDecimal.ts +++ b/src/smartcontracts/codec/managedDecimal.ts @@ -40,17 +40,12 @@ export class ManagedDecimalCodec { encodeNested(value: ManagedDecimalValue): Buffer { let buffers: Buffer[] = []; + const rawValue = new BigUIntValue(value.valueOf().shiftedBy(value.getScale())); if (value.isVariable()) { - buffers.push( - Buffer.from( - this.binaryCodec.encodeNested(new BigUIntValue(value.valueOf().shiftedBy(value.getScale()))), - ), - ); + buffers.push(Buffer.from(this.binaryCodec.encodeNested(rawValue))); buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale())))); } else { - buffers.push( - this.binaryCodec.encodeTopLevel(new BigUIntValue(value.valueOf().shiftedBy(value.getScale()))), - ); + buffers.push(this.binaryCodec.encodeTopLevel(rawValue)); } return Buffer.concat(buffers); } diff --git a/src/smartcontracts/codec/managedDecimalSigned.ts b/src/smartcontracts/codec/managedDecimalSigned.ts index 0f92fd7e..181d275b 100644 --- a/src/smartcontracts/codec/managedDecimalSigned.ts +++ b/src/smartcontracts/codec/managedDecimalSigned.ts @@ -41,21 +41,12 @@ export class ManagedDecimalSignedCodec { encodeNested(value: ManagedDecimalSignedValue): Buffer { let buffers: Buffer[] = []; + const rawValue = new BigIntValue(value.valueOf().shiftedBy(value.getScale())); if (value.isVariable()) { - buffers.push( - Buffer.from( - this.binaryCodec.encodeNested( - new BigIntValue(new BigNumber(value.valueOf().shiftedBy(value.getScale()))), - ), - ), - ); + buffers.push(Buffer.from(this.binaryCodec.encodeNested(rawValue))); buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale())))); } else { - buffers.push( - Buffer.from( - this.binaryCodec.encodeTopLevel(new BigIntValue(value.valueOf().shiftedBy(value.getScale()))), - ), - ); + buffers.push(Buffer.from(this.binaryCodec.encodeTopLevel(rawValue))); } return Buffer.concat(buffers); } diff --git a/src/smartcontracts/interaction.local.net.spec.ts b/src/smartcontracts/interaction.local.net.spec.ts index e1b546da..05b8a810 100644 --- a/src/smartcontracts/interaction.local.net.spec.ts +++ b/src/smartcontracts/interaction.local.net.spec.ts @@ -232,7 +232,7 @@ describe("test smart contract interactor", function () { .withSender(alice.address) .withValue(0); - // addition(); + // addition() let additionTransaction = additionInteraction .withSender(alice.address) .useThenIncrementNonceOf(alice.account) From 6d972bc7ea5ccb197981dac07bce31148d3e7267 Mon Sep 17 00:00:00 2001 From: danielailie Date: Mon, 16 Sep 2024 10:39:47 +0300 Subject: [PATCH 17/17] bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11b387c1..5c83ae96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "13.6.0", + "version": "13.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "13.6.0", + "version": "13.6.1", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", diff --git a/package.json b/package.json index 6a29957f..11211ca8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-core", - "version": "13.6.0", + "version": "13.6.1", "description": "MultiversX SDK for JavaScript and TypeScript", "author": "MultiversX", "homepage": "https://multiversx.com",